4 <title>Test EME syntax</title>
5 <script src="encrypted-media-utils.js"></script>
6 <script src="../../resources/testharness.js"></script>
7 <script src="../../resources/testharnessreport.js"></script>
12 // This function checks that calling |testCase.func| returns a
13 // rejected Promise with the error.name equal to
14 // |testCase.exception|.
15 function test_exception(testCase /*...*/)
17 var func = testCase.func;
18 var exception = testCase.exception;
19 var args = Array.prototype.slice.call(arguments, 1);
21 // Currently blink throws for TypeErrors rather than returning
22 // a rejected promise (http://crbug.com/359386).
23 // FIXME: Remove try/catch once they become failed promises.
25 return func.apply(null, args).then(
28 assert_unreached(format_value(func));
32 assert_equals(error.name, exception, format_value(func));
33 assert_not_equals(error.message, "", format_value(func));
37 // Only allow 'TypeError' exceptions to be thrown.
38 // Everything else should be a failed promise.
39 assert_equals('TypeError', exception, format_value(func));
40 assert_equals(e.name, exception, format_value(func));
44 var kCreateMediaKeysExceptionsTestCases = [
45 // Too few parameters.
47 exception: 'TypeError',
48 func: function() { return MediaKeys.create(); }
50 // Invalid parameters.
52 exception: 'InvalidAccessError',
53 func: function() { return MediaKeys.create(''); }
55 // Invalid key systems.
57 exception: 'NotSupportedError',
58 func: function() { return MediaKeys.create(null); }
61 exception: 'NotSupportedError',
62 func: function() { return MediaKeys.create(undefined); }
65 exception: 'NotSupportedError',
66 func: function() { return MediaKeys.create(1); }
69 exception: 'NotSupportedError',
70 func: function() { return MediaKeys.create(new Uint8Array(0)); }
73 exception: 'NotSupportedError',
74 func: function() { return MediaKeys.create('unsupported'); }
78 exception: 'NotSupportedError',
79 func: function() { return MediaKeys.create('org.w3\u263A.clearkey'); }
83 async_test(function(test)
85 // Since promises catch any exception and convert it into a
86 // rejected Promise, there is no current way to have the W3C
87 // test framework report a failed test. For now, simply force
88 // a timeout to indicate failure.
89 // FIXME: Once W3C test framework handles Promises, fix this.
91 var createPromises = kCreateMediaKeysExceptionsTestCases.map(function(testCase) {
92 return test_exception(testCase);
95 Promise.all(createPromises).then(function(result) {
97 }).catch(function(error) {
98 forceTestFailureFromPromise(test, error, 'create() tests failed');
100 }, 'Test MediaKeys create() exceptions.');
102 async_test(function(test)
104 assert_equals(typeof window.MediaKeys, 'function');
105 MediaKeys.create('org.w3.clearkey').then(function(mediaKeys) {
106 assert_not_equals(mediaKeys, null);
108 // Test creation of a second MediaKeys.
109 // The extra parameter is ignored.
110 return MediaKeys.create('org.w3.clearkey', 'extra');
111 }).then(function(mediaKeys) {
112 assert_not_equals(mediaKeys, null);
113 assert_equals(typeof mediaKeys, 'object');
114 assert_equals(mediaKeys.keySystem, 'org.w3.clearkey');
115 assert_equals(typeof mediaKeys.createSession, 'function');
116 assert_equals(typeof mediaKeys.addEventListener, 'undefined');
118 }).catch(function(error) {
119 forceTestFailureFromPromise(test, error, 'create() tests failed');
121 }, 'Test MediaKeys create().');
123 var kCreateSessionExceptionsTestCases = [
124 // Tests in this set use a shortened parameter name due to
125 // format_value() only returning the first 60 characters as the
126 // result. With a longer name the first 60 characters is not
127 // enough to determine which test failed.
129 // Invalid parameters.
131 exception: 'TypeError',
132 func: function(mk) { return mk.createSession(); }
135 exception: 'TypeError',
136 func: function(mk) { return mk.createSession(''); }
139 exception: 'TypeError',
140 func: function(mk) { return mk.createSession(null); }
143 exception: 'TypeError',
144 func: function(mk) { return mk.createSession(undefined); }
147 exception: 'TypeError',
148 func: function(mk) { return mk.createSession(1); }
151 exception: 'TypeError',
152 func: function(mk) { return mk.createSession(new Uint8Array(0)); }
155 exception: 'TypeError',
156 func: function(mk) { return mk.createSession('TEMPORARY'); }
160 async_test(function(test)
162 MediaKeys.create('org.w3.clearkey').then(function(mediaKeys) {
163 var sessionPromises = kCreateSessionExceptionsTestCases.map(function(testCase) {
164 return test_exception(testCase, mediaKeys);
167 assert_not_equals(sessionPromises.length, 0);
168 return Promise.all(sessionPromises);
169 }).then(function(result) {
171 }).catch(function(error) {
172 forceTestFailureFromPromise(test, error, 'createSession() tests failed');
174 }, 'Test MediaKeys createSession() exceptions.');
176 var kGenerateRequestExceptionsTestCases = [
177 // Tests in this set use a shortened parameter name due to
178 // format_value() only returning the first 60 characters as the
179 // result. With a longer name the first 60 characters is not
180 // enough to determine which test failed. Even with the
181 // shortened name, the error message for the last couple of
182 // tests is the same.
183 // Too few parameters.
185 exception: 'TypeError',
186 func: function(mk1) { return mk1.createSession().generateRequest(); }
189 exception: 'TypeError',
190 func: function(mk2) { return mk2.createSession().generateRequest(''); }
193 exception: 'TypeError',
194 func: function(mk3) { return mk3.createSession().generateRequest(null); }
197 exception: 'TypeError',
198 func: function(mk4) { return mk4.createSession().generateRequest(undefined); }
201 exception: 'TypeError',
202 func: function(mk5) { return mk5.createSession().generateRequest(1); }
205 exception: 'TypeError',
206 func: function(mk6) { return mk6.createSession().generateRequest(new Uint8Array(0)); }
209 exception: 'TypeError',
210 func: function(mk7, _, initData) { return mk7.createSession().generateRequest(initData); }
212 // Invalid parameters.
214 exception: 'InvalidAccessError',
215 func: function(mk8, _, initData) { return mk8.createSession().generateRequest('', initData); }
217 // Not supported initDataTypes.
219 exception: 'NotSupportedError',
220 func: function(mk9, _, initData) { return mk9.createSession().generateRequest(null, initData); }
223 exception: 'NotSupportedError',
224 func: function(mk10, _, initData) { return mk10.createSession().generateRequest(undefined, initData); }
227 exception: 'NotSupportedError',
228 func: function(mk11, _, initData) { return mk11.createSession().generateRequest(1, initData); }
231 exception: 'NotSupportedError',
232 func: function(mk12, _, initData) { return mk12.createSession().generateRequest(new Uint8Array(0), initData); }
235 exception: 'NotSupportedError',
236 func: function(mk13, _, initData) { return mk13.createSession().generateRequest('unsupported', initData); }
239 exception: 'NotSupportedError',
240 func: function(mk14, _, initData) { return mk14.createSession().generateRequest('video/foo', initData); }
243 exception: 'NotSupportedError',
244 func: function(mk15, _, initData) { return mk15.createSession().generateRequest('text/webm', initData); }
246 // FIXME: Enable when switching to initDataType from MIME type.
247 // http://crbug.com/385874.
249 // exception: 'NotSupportedError',
250 // func: function(mk16, _, initData) { return mk16.createSession('video/webm', initData); }
254 var kTypeSpecificGenerateRequestExceptionsTestCases = [
255 // Tests in this set use a shortened parameter name due to
256 // format_value() only returning the first 60 characters as the
257 // result. With a longer name the first 60 characters is not
258 // enough to determine which test failed. Even with the
259 // shortened name, the error message for the last couple of
260 // tests is the same.
262 // Too few parameters.
264 exception: 'TypeError',
265 func: function(mk1, type) { return mk1.createSession().generateRequest(type); }
267 // Invalid parameters.
269 exception: 'TypeError',
270 func: function(mk2, type) { return mk2.createSession().generateRequest(type, ''); }
273 exception: 'TypeError',
274 func: function(mk3, type) { return mk3.createSession().generateRequest(type, null); }
277 exception: 'TypeError',
278 func: function(mk4, type) { return mk4.createSession().generateRequest(type, undefined); }
281 exception: 'TypeError',
282 func: function(mk5, type) { return mk5.createSession().generateRequest(type, 1); }
285 exception: 'InvalidAccessError',
286 func: function(mk6, type) { return mk6.createSession().generateRequest(type, new Uint8Array(0)); }
290 async_test(function(test)
292 MediaKeys.create('org.w3.clearkey').then(function(mediaKeys) {
293 // FIXME: Remove "video/" from the calls to isTypeSupported() once it is updated.
294 // http://crbug.com/405731.
295 var initData = stringToUint8Array('init data');
296 var sessionPromises = kGenerateRequestExceptionsTestCases.map(function(testCase) {
297 return test_exception(testCase, mediaKeys, '', initData);
300 // Test that WebM sessions generate the expected error, if
302 if (MediaKeys.isTypeSupported('org.w3.clearkey', 'video/webm')) {
303 var WebmSessionPromises = kTypeSpecificGenerateRequestExceptionsTestCases.map(function(testCase) {
304 return test_exception(testCase, mediaKeys, 'webm', getInitData('webm'));
306 sessionPromises = sessionPromises.concat(WebmSessionPromises);
309 // Repeat for MP4, if supported.
310 if (MediaKeys.isTypeSupported('org.w3.clearkey', 'video/mp4')) {
311 var mp4SessionPromises = kTypeSpecificGenerateRequestExceptionsTestCases.map(function(testCase) {
312 return test_exception(testCase, mediaKeys, 'cenc', getInitData('cenc'));
314 sessionPromises = sessionPromises.concat(mp4SessionPromises);
317 assert_not_equals(sessionPromises.length, 0);
318 return Promise.all(sessionPromises);
319 }).then(function(result) {
321 }).catch(function(error) {
322 forceTestFailureFromPromise(test, error, 'generateRequest() tests failed');
324 }, 'Test MediaKeys generateRequest() exceptions.');
326 // All calls to |func| in this group are supposed to succeed.
327 // However, the spec notes that some things are optional for
328 // Clear Key. In particular, support for persistent sessions
329 // is optional. Since some implementations won't support some
330 // features, a NotSupportedError is treated as a success
331 // if |isNotSupportedAllowed| is true.
332 var kCreateSessionTestCases = [
333 // Use the default sessionType.
335 func: function(mk) { return mk.createSession(); },
336 isNotSupportedAllowed: false
338 // Try variations of sessionType.
340 func: function(mk) { return mk.createSession('temporary'); },
341 isNotSupportedAllowed: false
344 func: function(mk) { return mk.createSession(undefined); },
345 isNotSupportedAllowed: false
348 // Since this is optional, some Clear Key implementations
349 // will succeed, others will return a "NotSupportedError".
350 // Both are allowed results.
351 func: function(mk) { return mk.createSession('persistent'); },
352 isNotSupportedAllowed: true
354 // Try additional parameter, which should be ignored.
356 func: function(mk) { return mk.createSession('temporary', 'extra'); },
357 isNotSupportedAllowed: false
361 // This function checks that calling |testCase.func| creates a
362 // MediaKeySession object with some default values. It also
363 // allows for an NotSupportedError to be generated and treated as a
364 // success, if allowed. See comment above kCreateSessionTestCases.
365 function test_createSession(testCase, mediaKeys)
369 mediaKeySession = testCase.func.call(null, mediaKeys);
371 assert_true(testCase.isNotSupportedAllowed);
375 // FIXME: Update this set of tests when done
376 // implementing the latest spec.
377 assert_equals(typeof mediaKeySession, 'object');
378 assert_equals(typeof mediaKeySession.addEventListener, 'function');
379 assert_equals(typeof mediaKeySession.generateRequest, 'function');
380 assert_equals(typeof mediaKeySession.update, 'function');
381 assert_equals(typeof mediaKeySession.release, 'function');
382 assert_equals(mediaKeySession.error, null);
383 assert_equals(mediaKeySession.sessionId, '');
384 assert_equals(typeof mediaKeySession.sessionId, 'string');
385 assert_equals(typeof mediaKeySession.onopen, 'undefined');
386 assert_equals(typeof mediaKeySession.onmessage, 'undefined');
387 assert_equals(typeof mediaKeySession.onclose, 'undefined');
388 assert_equals(typeof mediaKeySession.onerror, 'undefined');
391 async_test(function(test)
393 MediaKeys.create('org.w3.clearkey').then(function(mediaKeys) {
394 kCreateSessionTestCases.map(function(testCase) {
395 test_createSession(testCase, mediaKeys);
398 }).catch(function(error) {
399 forceTestFailureFromPromise(test, error, 'createSession() tests failed');
401 }, 'Test MediaKeys createSession().');
403 // This function checks that calling generateRequest() works for
404 // various sessions. |testCase.func| creates a MediaKeySession
405 // object, and then generateRequest() is called on that object. It
406 // allows for an NotSupportedError to be generated and treated as a
407 // success, if allowed. See comment above kCreateSessionTestCases.
408 function test_generateRequest(testCase, mediaKeys, type, initData)
412 mediaKeySession = testCase.func.call(null, mediaKeys);
414 assert_true(testCase.isNotSupportedAllowed);
415 return new Promise(new Promise(function() {}));
417 return mediaKeySession.generateRequest(type, initData);
420 async_test(function(test)
422 MediaKeys.create('org.w3.clearkey').then(function(mediaKeys) {
423 var sessionPromises = [];
425 // Test that WebM sessions generate the expected error, if
427 if (MediaKeys.isTypeSupported('org.w3.clearkey', 'video/webm')) {
428 var WebmSessionPromises = kCreateSessionTestCases.map(function(testCase) {
429 return test_generateRequest(testCase, mediaKeys, 'webm', getInitData('webm'));
431 sessionPromises = sessionPromises.concat(WebmSessionPromises);
434 // Repeat for MP4, if supported.
435 if (MediaKeys.isTypeSupported('org.w3.clearkey', 'video/mp4')) {
436 var mp4SessionPromises = kCreateSessionTestCases.map(function(testCase) {
437 return test_generateRequest(testCase, mediaKeys, 'cenc', getInitData('cenc'));
439 sessionPromises = sessionPromises.concat(mp4SessionPromises);
442 assert_not_equals(sessionPromises.length, 0);
443 return Promise.all(sessionPromises);
444 }).then(function(result) {
446 }).catch(function(error) {
447 forceTestFailureFromPromise(test, error, 'generateRequest() tests failed');
449 }, 'Test MediaKeys generateRequest().');
451 var kUpdateSessionExceptionsTestCases = [
452 // Tests in this set use a shortened parameter name due to
453 // format_value() only returning the first 60 characters as the
454 // result. With a longer name (mediaKeySession) the first 60
455 // characters is not enough to determine which test failed.
457 // Too few parameters.
459 exception: 'TypeError',
460 func: function(s) { return s.update(); }
462 // Invalid parameters.
464 exception: 'TypeError',
465 func: function(s) { return s.update(''); }
468 exception: 'TypeError',
469 func: function(s) { return s.update(null); }
472 exception: 'TypeError',
473 func: function(s) { return s.update(undefined); }
476 exception: 'TypeError',
477 func: function(s) { return s.update(1); }
480 exception: 'InvalidAccessError',
481 func: function(s) { return s.update(new Uint8Array(0)); }
485 async_test(function(test)
487 MediaKeys.create('org.w3.clearkey').then(function(mediaKeys) {
490 if (MediaKeys.isTypeSupported('org.w3.clearkey', 'video/webm')) {
491 var WebmSessionPromises = kUpdateSessionExceptionsTestCases.map(function(testCase) {
492 var mediaKeySession = mediaKeys.createSession();
493 return mediaKeySession.generateRequest('webm', getInitData('webm')).then(function(result) {
494 return test_exception(testCase, mediaKeySession);
497 promises = promises.concat(WebmSessionPromises);
500 if (MediaKeys.isTypeSupported('org.w3.clearkey', 'video/mp4')) {
501 var mp4SessionPromises = kUpdateSessionExceptionsTestCases.map(function(testCase) {
502 var mediaKeySession = mediaKeys.createSession();
503 return mediaKeySession.generateRequest('cenc', getInitData('cenc')).then(function(result) {
504 return test_exception(testCase, mediaKeySession);
507 promises = promises.concat(mp4SessionPromises);
510 assert_not_equals(promises.length, 0);
511 return Promise.all(promises);
512 }).then(function(result) {
514 }).catch(function(error) {
515 forceTestFailureFromPromise(test, error, 'update() tests failed');
517 }, 'Test MediaKeySession update() exceptions.');
519 function create_update_test(mediaKeys, type, initData)
521 var mediaKeySession = mediaKeys.createSession();
522 var promise = mediaKeySession.generateRequest(type, initData).then(function(result) {
523 var validLicense = stringToUint8Array(createJWKSet(createJWK(stringToUint8Array('123'), stringToUint8Array('1234567890abcdef'))));
524 return mediaKeySession.update(validLicense);
525 }).then(function(result) {
526 // Call update() with a different license and an extra
527 // parameter. The extra parameter is ignored.
528 var validLicense = stringToUint8Array(createJWKSet(createJWK(stringToUint8Array('4567890'), stringToUint8Array('01234567890abcde'))));
529 return mediaKeySession.update(validLicense, 'extra');
534 async_test(function(test)
536 MediaKeys.create('org.w3.clearkey').then(function(mediaKeys) {
539 if (MediaKeys.isTypeSupported('org.w3.clearkey', 'video/webm')) {
540 promises.push(create_update_test(mediaKeys, 'webm', getInitData('webm')));
543 if (MediaKeys.isTypeSupported('org.w3.clearkey', 'video/mp4')) {
544 promises.push(create_update_test(mediaKeys, 'cenc', getInitData('cenc')));
547 assert_not_equals(promises.length, 0);
548 return Promise.all(promises);
549 }).then(function(result) {
551 }).catch(function(error) {
552 forceTestFailureFromPromise(test, error, 'update() tests failed');
554 }, 'Test MediaKeySession update().');
556 function create_release_test(mediaKeys, type, initData)
558 var mediaKeySession = mediaKeys.createSession();
559 var promise = mediaKeySession.generateRequest(type, initData).then(function(result) {
560 return mediaKeySession.release();
561 // FIXME: Uncomment once the code supports multiple release() calls.
562 // }).then(function(result) {
563 // // Call release() again with an extra parameter. The extra
564 // // parameter is ignored.
565 // return mediaKeySession.release('extra');
570 async_test(function(test)
572 MediaKeys.create('org.w3.clearkey').then(function(mediaKeys) {
575 if (MediaKeys.isTypeSupported('org.w3.clearkey', 'video/webm')) {
576 promises.push(create_release_test(mediaKeys, 'webm', getInitData('webm')));
579 if (MediaKeys.isTypeSupported('org.w3.clearkey', 'video/mp4')) {
580 promises.push(create_release_test(mediaKeys, 'cenc', getInitData('cenc')));
583 assert_not_equals(promises.length, 0);
584 return Promise.all(promises);
585 }).then(function(result) {
587 }).catch(function(error) {
588 forceTestFailureFromPromise(test, error, 'release() tests failed');
590 }, 'Test MediaKeySession release().');
592 // FIXME: Add syntax checks for MediaKeys.IsTypeSupported().
593 // FIXME: Add syntax checks for MediaKeyError and MediaKeySession events.
594 // FIXME: Add HTMLMediaElement syntax checks, e.g. setMediaKeys, mediakeys, onneedkey.