Home | History | Annotate | Download | only in w3c
      1 // Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
      2 //
      3 // Use of this source code is governed by a BSD-style license
      4 // that can be found in the LICENSE file in the root of the source
      5 // tree. An additional intellectual property rights grant can be found
      6 // in the file PATENTS.  All contributing project authors may
      7 // be found in the AUTHORS file in the root of the source tree.
      8 
      9 setup({timeout:5000});
     10 
     11 // Helper functions to minimize code duplication.
     12 function failedCallback(test) {
     13   return test.step_func(function (error) {
     14     assert_unreached('Should not get an error callback');
     15   });
     16 }
     17 function invokeGetUserMedia(test, okCallback) {
     18   navigator.getUserMedia({ video: true, audio: true }, okCallback,
     19       failedCallback(test));
     20 }
     21 
     22 function createInvisibleVideoTag() {
     23   var video = document.createElement('video');
     24   video.autoplay = true;
     25   video.style.display = 'none';
     26   document.body.appendChild(video);
     27   return video;
     28 }
     29 
     30 // 4.2 MediaStream.
     31 var mediaStreamTest = async_test('4.2 MediaStream');
     32 
     33 function verifyMediaStream(stream) {
     34   // TODO(kjellander): Add checks for default values where applicable.
     35   test(function () {
     36     assert_own_property(stream, 'id');
     37     assert_true(typeof stream.id === 'string');
     38     assert_readonly(stream, 'id');
     39   }, '[MediaStream] id attribute');
     40 
     41   test(function () {
     42     assert_inherits(stream, 'getAudioTracks');
     43     assert_true(typeof stream.getAudioTracks === 'function');
     44   }, '[MediaStream] getAudioTracks function');
     45 
     46   test(function () {
     47     assert_inherits(stream, 'getVideoTracks');
     48     assert_true(typeof stream.getVideoTracks === 'function');
     49   }, '[MediaStream] getVideoTracks function');
     50 
     51   test(function () {
     52     assert_inherits(stream, 'getTrackById');
     53     assert_true(typeof stream.getTrackById === 'function');
     54   }, '[MediaStream] getTrackById function');
     55 
     56   test(function () {
     57     assert_inherits(stream, 'addTrack');
     58     assert_true(typeof stream.addTrack === 'function');
     59   }, '[MediaStream] addTrack function');
     60 
     61   test(function () {
     62     assert_inherits(stream, 'removeTrack');
     63     assert_true(typeof stream.removeTrack === 'function');
     64   }, '[MediaStream] removeTrack function');
     65 
     66   test(function () {
     67     assert_inherits(stream, 'clone');
     68     assert_true(typeof stream.clone === 'function');
     69   }, '[MediaStream] clone function');
     70 
     71   test(function () {
     72     assert_own_property(stream, 'active');
     73     assert_true(typeof stream.active === 'boolean');
     74     assert_readonly(stream, 'active');
     75   }, '[MediaStream] active attribute');
     76 
     77   test(function () {
     78     assert_own_property(stream, 'onactive');
     79     assert_true(stream.onactive === null);
     80   }, '[MediaStream] onactive EventHandler');
     81 
     82   test(function () {
     83     assert_own_property(stream, 'oninactive');
     84     assert_true(stream.oninactive === null);
     85   }, '[MediaStream] oninactive EventHandler');
     86 
     87   test(function () {
     88     assert_own_property(stream, 'onaddtrack');
     89     assert_true(stream.onaddtrack === null);
     90   }, '[MediaStream] onaddtrack EventHandler');
     91 
     92   test(function () {
     93     assert_own_property(stream, 'onremovetrack');
     94     assert_true(stream.onremovetrack === null);
     95   }, '[MediaStream] onremovetrack EventHandler');
     96 }
     97 
     98 mediaStreamTest.step(function() {
     99   var okCallback = mediaStreamTest.step_func(function (stream) {
    100     verifyMediaStream(stream);
    101     var videoTracks = stream.getVideoTracks();
    102     assert_true(videoTracks.length > 0);
    103     mediaStreamTest.done();
    104   });
    105 
    106   invokeGetUserMedia(mediaStreamTest, okCallback);
    107 });
    108 
    109 var mediaStreamCallbacksTest = async_test('4.2.2 MediaStream callbacks');
    110 
    111 mediaStreamCallbacksTest.step(function() {
    112   var addCallbackCalled = false;
    113   var onAddTrackCallback = mediaStreamCallbacksTest.step_func(function (event) {
    114     assert_true(event.track instanceof MediaStreamTrack);
    115     addCallbackCalled = true;
    116   });
    117   var onRemoveTrackCallback =
    118       mediaStreamCallbacksTest.step_func(function (event) {
    119     assert_true(event.track instanceof MediaStreamTrack);
    120     assert_true(addCallbackCalled, 'Add should have been called after remove.');
    121     mediaStreamCallbacksTest.done();
    122   });
    123   var okCallback = mediaStreamCallbacksTest.step_func(function (stream) {
    124     var videoTracks = stream.getVideoTracks();
    125 
    126     // Verify event handlers are working.
    127     stream.onaddtrack = onAddTrackCallback;
    128     stream.onremovetrack = onRemoveTrackCallback;
    129     stream.removeTrack(videoTracks[0]);
    130     stream.addTrack(videoTracks[0]);
    131   });
    132 
    133   invokeGetUserMedia(mediaStreamCallbacksTest, okCallback);
    134 });
    135 
    136 // TODO(phoglund): add test for onactive/oninactive.
    137 
    138 // 4.3 MediaStreamTrack.
    139 var mediaStreamTrackTest = async_test('4.3 MediaStreamTrack');
    140 
    141 function verifyTrack(type, track) {
    142   test(function () {
    143     assert_own_property(track, 'kind');
    144     assert_readonly(track, 'kind');
    145     assert_true(typeof track.kind === 'string',
    146         'kind is an object (DOMString)');
    147   }, '[MediaStreamTrack (' + type + ')] kind attribute');
    148 
    149   test(function () {
    150     assert_own_property(track, 'id');
    151     assert_readonly(track, 'id');
    152     assert_true(typeof track.id === 'string',
    153         'id is an object (DOMString)');
    154   }, '[MediaStreamTrack (' + type + ')] id attribute');
    155 
    156   test(function () {
    157     assert_own_property(track, 'label');
    158     assert_readonly(track, 'label');
    159     assert_true(typeof track.label === 'string',
    160         'label is an object (DOMString)');
    161   }, '[MediaStreamTrack (' + type + ')] label attribute');
    162 
    163   test(function () {
    164     assert_own_property(track, 'enabled');
    165     assert_true(typeof track.enabled === 'boolean');
    166     assert_true(track.enabled, 'enabled property must be true initially');
    167   }, '[MediaStreamTrack (' + type + ')] enabled attribute');
    168 
    169   test(function () {
    170     assert_own_property(track, 'muted');
    171     assert_readonly(track, 'muted');
    172     assert_true(typeof track.muted === 'boolean');
    173     assert_false(track.muted, 'muted property must be false initially');
    174   }, '[MediaStreamTrack (' + type + ')] muted attribute');
    175 
    176   test(function () {
    177     assert_own_property(track, 'onmute');
    178     assert_true(track.onmute === null);
    179   }, '[MediaStreamTrack (' + type + ')] onmute EventHandler');
    180 
    181   test(function () {
    182     assert_own_property(track, 'onunmute');
    183     assert_true(track.onunmute === null);
    184   }, '[MediaStreamTrack (' + type + ')] onunmute EventHandler');
    185 
    186   test(function () {
    187     assert_own_property(track, '_readonly');
    188     assert_readonly(track, '_readonly');
    189     assert_true(typeof track._readonly === 'boolean');
    190   }, '[MediaStreamTrack (' + type + ')] _readonly attribute');
    191 
    192   test(function () {
    193     assert_own_property(track, 'remote');
    194     assert_readonly(track, 'remote');
    195     assert_true(typeof track.remote === 'boolean');
    196   }, '[MediaStreamTrack (' + type + ')] remote attribute');
    197 
    198   test(function () {
    199     assert_own_property(track, 'readyState');
    200     assert_readonly(track, 'readyState');
    201     assert_true(typeof track.readyState === 'string');
    202     // TODO(kjellander): verify the initial state.
    203   }, '[MediaStreamTrack (' + type + ')] readyState attribute');
    204 
    205   test(function () {
    206     assert_own_property(track, 'onstarted');
    207     assert_true(track.onstarted === null);
    208   }, '[MediaStreamTrack (' + type + ')] onstarted EventHandler');
    209 
    210   test(function () {
    211     assert_own_property(track, 'onended');
    212     assert_true(track.onended === null);
    213   }, '[MediaStreamTrack (' + type + ')] onended EventHandler');
    214 
    215   test(function () {
    216     assert_inherits(track, 'getNativeSettings');
    217     assert_true(typeof track.capabilities === 'function');
    218   }, '[MediaStreamTrack (' + type + ')]: getNativeSettings function');
    219 
    220   test(function () {
    221     assert_inherits(track, 'clone');
    222     assert_true(typeof track.clone === 'function');
    223   }, '[MediaStreamTrack (' + type + ')] clone function');
    224 
    225   test(function () {
    226     assert_inherits(track, 'stop');
    227     assert_true(typeof track.stop === 'function');
    228   }, '[MediaStreamTrack (' + type + ')] stop function');
    229 
    230   test(function () {
    231     assert_inherits(track, 'getCapabilities');
    232     assert_true(typeof track.capabilities === 'function');
    233   }, '[MediaStreamTrack (' + type + ')]: getCapabilities function');
    234 
    235   test(function () {
    236     assert_inherits(track, 'getConstraints');
    237     assert_true(typeof track.constraints === 'function');
    238   }, '[MediaStreamTrack (' + type + ')]: getConstraints function');
    239 
    240   test(function () {
    241     assert_inherits(track, 'getSettings');
    242     assert_true(typeof track.constraints === 'function');
    243   }, '[MediaStreamTrack (' + type + ')]: getSettings function');
    244 
    245   test(function () {
    246     assert_inherits(track, 'applyConstraints');
    247     assert_true(typeof track.applyConstraints === 'function');
    248   }, '[MediaStreamTrack (' + type + ')]: applyConstraints function');
    249 };
    250 
    251 mediaStreamTrackTest.step(function() {
    252   var okCallback = mediaStreamTrackTest.step_func(function (stream) {
    253     verifyTrack('audio', stream.getAudioTracks()[0]);
    254     verifyTrack('video', stream.getVideoTracks()[0]);
    255     mediaStreamTrackTest.done();
    256   });
    257   invokeGetUserMedia(mediaStreamTrackTest, okCallback);
    258 });
    259 
    260 mediaStreamTrackTest.step(function() {
    261   var okCallback = mediaStreamTrackTest.step_func(function (stream) {
    262     // Verify event handlers are working.
    263     var track = stream.getVideoTracks()[0];
    264     track.onended = onEndedCallback
    265     track.stop();
    266     mediaStreamTrackTest.done();
    267   });
    268   var onEndedCallback = mediaStreamTrackTest.step_func(function () {
    269     assert_true(track.ended);
    270     mediaStreamTrackTest.done();
    271   });
    272   invokeGetUserMedia(mediaStreamTrackTest, okCallback);
    273 });
    274 
    275 // 4.4 MediaStreamTrackEvent tests.
    276 var mediaStreamTrackEventTest = async_test('4.4 MediaStreamTrackEvent');
    277 mediaStreamTrackEventTest.step(function() {
    278   var okCallback = mediaStreamTrackEventTest.step_func(function (stream) {
    279     // TODO(kjellander): verify attributes.
    280     mediaStreamTrackEventTest.done();
    281   });
    282   invokeGetUserMedia(mediaStreamTrackEventTest, okCallback);
    283 });
    284 
    285 // 6. Media streams as media elements.
    286 
    287 var playingInMediaElementTest = async_test(
    288     '6.2 Loading and Playing a MediaStream in a Media Element');
    289 playingInMediaElementTest.step(function() {
    290   var video = createInvisibleVideoTag();
    291 
    292   var okCallback = playingInMediaElementTest.step_func(function (stream) {
    293     video.onplay = playingInMediaElementTest.step_func(function() {
    294       // This depends on what webcam we're actually running with, but the
    295       // resolution should at least be greater than or equal to QVGA.
    296       assert_greater_than_equal(video.videoWidth, 320);
    297       assert_greater_than_equal(video.videoHeight, 240);
    298 
    299       playingInMediaElementTest.done();
    300     });
    301     video.srcObject = stream;
    302   });
    303   invokeGetUserMedia(playingInMediaElementTest, okCallback);
    304 });
    305 
    306 // Verifies a media element track (for instance belonging to a video tag)
    307 // after it has been assigned a media stream.
    308 function verifyOneMediaElementTrack(track, correspondingMediaStreamTrack) {
    309   assert_equals(track.id, correspondingMediaStreamTrack.id);
    310   assert_equals(track.kind, 'main');
    311   assert_equals(track.label, correspondingMediaStreamTrack.label);
    312   assert_equals(track.language, '');
    313 }
    314 
    315 var setsUpMediaTracksRightTest = async_test(
    316     '6.2 Sets up <video> audio and video tracks right');
    317 setsUpMediaTracksRightTest.step(function() {
    318   var video = createInvisibleVideoTag();
    319 
    320   var okCallback = setsUpMediaTracksRightTest.step_func(function (stream) {
    321     video.onplay = setsUpMediaTracksRightTest.step_func(function() {
    322       // Verify the requirements on the video tag's streams as outlined in 6.2.
    323       // There could be any number of tracks depending on what device we have
    324       // connected, so verify all of them. There should be at least one of audio
    325       // and video each though.
    326       assert_inherits(video, 'videoTracks',
    327                       'Browser missing videoTracks support on media elements.');
    328       assert_readonly(video, 'videoTracks');
    329       assert_greater_than_equal(video.videoTracks.length, 1);
    330       assert_equals(video.videoTracks.length, stream.getVideoTracks().length);
    331 
    332       for (var i = 0; i < video.videoTracks.length; i++) {
    333         verifyOneMediaElementTrack(video.videoTracks[i],
    334                                    stream.getVideoTracks()[i]);
    335       }
    336 
    337       assert_inherits(video, 'audioTracks',
    338                       'Browser missing audioTracks support on media elements.');
    339       assert_readonly(video, 'audioTracks');
    340       assert_greater_than_equal(video.audioTracks.length, 1);
    341       assert_equals(video.audioTracks.length, stream.getAudioTracks().length);
    342 
    343       for (var i = 0; i < video.audioTracks.length; i++) {
    344         verifyOneMediaElementTrack(audio.audioTracks[i],
    345                                    stream.getAudioTracks()[i]);
    346       }
    347 
    348       setsUpMediaTracksRightTest.done();
    349     });
    350     video.srcObject = stream;
    351   });
    352   invokeGetUserMedia(setsUpMediaTracksRightTest, okCallback);
    353 });
    354 
    355 var mediaElementsTest =
    356   async_test('6.3 Media Element Attributes when Playing a MediaStream');
    357 
    358 function verifyVideoTagWithStream(videoTag) {
    359   test(function () {
    360     assert_equals(videoTag.currentSrc, '');
    361   }, '[Video tag] currentSrc attribute');
    362 
    363   test(function () {
    364     assert_equals(videoTag.preload, 'none');
    365   }, '[Video tag] preload attribute');
    366 
    367   test(function () {
    368     assert_equals(videoTag.buffered.length, 0);
    369   }, '[Video tag] buffered attribute');
    370 
    371   test(function () {
    372     // Where 1 is NETWORK_IDLE.
    373     assert_equals(videoTag.networkState, 1);
    374   }, '[Video tag] networkState attribute');
    375 
    376   test(function () {
    377     // 0 is HAVE_NOTHING, 4 is HAVE_ENOUGH_DATA.
    378     assert_true(videoTag.readyState == 0 || videoTag.readyState == 4);
    379   }, '[Video tag] readyState attribute');
    380 
    381   test(function () {
    382     assert_true(videoTag.currentTime >= 0);
    383     assert_throws(
    384         'InvalidStateError', function () { videoTag.currentTime = 1234; },
    385         'Attempts to modify currentTime shall throw InvalidStateError');
    386   }, '[Video tag] currentTime attribute');
    387 
    388   test(function () {
    389     assert_equals(videoTag.duration, Infinity, 'videoTag.duration');
    390   }, '[Video tag] duration attribute');
    391 
    392   test(function () {
    393     assert_false(videoTag.seeking, 'videoTag.seeking');
    394   }, '[Video tag] seeking attribute');
    395 
    396   test(function () {
    397     assert_equals(videoTag.defaultPlaybackRate, 1.0);
    398     assert_throws(
    399         'DOMException', function () { videoTag.defaultPlaybackRate = 2.0; },
    400         'Attempts to alter videoTag.defaultPlaybackRate MUST fail');
    401   }, '[Video tag] defaultPlaybackRate attribute');
    402 
    403   test(function () {
    404     assert_equals(videoTag.playbackRate, 1.0);
    405     assert_throws(
    406         'DOMException', function () { videoTag.playbackRate = 2.0; },
    407         'Attempts to alter videoTag.playbackRate MUST fail');
    408   }, '[Video tag] playbackRate attribute');
    409 
    410   test(function () {
    411     assert_equals(videoTag.played.length, 1, 'videoTag.played.length');
    412     assert_equals(videoTag.played.start(0), 0);
    413     assert_true(videoTag.played.end(0) >= videoTag.currentTime);
    414   }, '[Video tag] played attribute');
    415 
    416   test(function () {
    417     assert_equals(videoTag.seekable.length, 0);
    418     // This is wrong in the standard: start() and end() must have arguments, but
    419     // since the time range is empty as we assert in the line above, there is no
    420     // valid argument with which we can call the methods.
    421     // assert_equals(videoTag.seekable.start(), videoTag.currentTime);
    422     // assert_equals(videoTag.seekable.end(), videoTag.currentTime);
    423   }, '[Video tag] seekable attribute');
    424 
    425   test(function () {
    426     assert_equals(videoTag.startDate, NaN, 'videoTag.startDate');
    427   }, '[Video tag] startDate attribute');
    428 
    429   test(function () {
    430     assert_false(videoTag.loop);
    431   }, '[Video tag] loop attribute');
    432 
    433 };
    434 
    435 mediaElementsTest.step(function() {
    436   var okCallback = mediaElementsTest.step_func(function (stream) {
    437     var video = createInvisibleVideoTag();
    438     video.srcObject = stream;
    439     verifyVideoTagWithStream(video);
    440     mediaElementsTest.done();
    441   });
    442   invokeGetUserMedia(mediaElementsTest, okCallback);
    443 });
    444 
    445 // 9. Enumerating local media devices.
    446 // TODO(phoglund): add tests.
    447 
    448 // 10. Obtaining local multimedia content.
    449 
    450 function testGetUserMedia(test, constraints) {
    451   var okCallback = test.step_func(function (stream) {
    452     assert_true(stream !== null);
    453     test.done();
    454   });
    455   navigator.getUserMedia(constraints, okCallback, failedCallback(test));
    456 }
    457 
    458 var getUserMediaTestAudioVideo = async_test('10.1.1 NavigatorUserMedia A/V');
    459 getUserMediaTestAudioVideo.step(function() {
    460   testGetUserMedia(getUserMediaTestAudioVideo, { video: true, audio: true });
    461 });
    462 
    463 var getUserMediaTestVideo = async_test('10.1.1 NavigatorUserMedia V');
    464 getUserMediaTestVideo.step(function() {
    465   testGetUserMedia(getUserMediaTestVideo, { video: true, audio: false });
    466 });
    467 
    468 var getUserMediaTestAudio = async_test('10.1.1 NavigatorUserMedia A');
    469 getUserMediaTestAudio.step(function() {
    470   testGetUserMedia(getUserMediaTestAudio, { video: false, audio: true });
    471 });
    472 
    473 var getUserMediaTestNull = async_test('10.1.1 NavigatorUserMedia Null');
    474 getUserMediaTestNull.step(function() {
    475   testGetUserMedia(getUserMediaTestNull, null);
    476 });
    477 
    478 var getUserMediaTestPeerIdentity =
    479     async_test('10.2 NavigatorUserMedia with peerIdentity');
    480 getUserMediaTestPeerIdentity.step(function() {
    481   var peerIdentity = 'my_identity';
    482   var okCallback = getUserMediaTestPeerIdentity.step_func(function (stream) {
    483     assert_true(stream !== null);
    484     stream.getVideoTracks().forEach(function(track) {
    485       assert_equals(track.peerIdentity, peerIdentity);
    486     });
    487     stream.getAudioTracks().forEach(function(track) {
    488       assert_equals(track.peerIdentity, peerIdentity);
    489     });
    490     getUserMediaTestPeerIdentity.done();
    491   });
    492   navigator.getUserMedia(
    493       {video: true, audio: true, peerIdentity: 'my_identity' },
    494       okCallback, failedCallback(getUserMediaTestPeerIdentity));
    495 });
    496 
    497 // 10.2 MediaStreamConstraints.
    498 var constraintsTest = async_test('10.2 MediaStreamConstraints');
    499 constraintsTest.step(function() {
    500   var okCallback = constraintsTest.step_func(function (stream) {
    501     assert_true(stream !== null);
    502     constraintsTest.done();
    503   });
    504 
    505   // See https://googlechrome.github.io/webrtc/samples/web/content/constraints/
    506   // for more examples of constraints.
    507   // TODO(phoglund): test more constraints; the possibilities here are endless.
    508   var constraints = {};
    509   constraints.audio = true;
    510   constraints.video = { mandatory: {}, optional: [] };
    511   constraints.video.mandatory.minWidth = 640;
    512   constraints.video.mandatory.minHeight = 480;
    513   constraints.video.mandatory.minFrameRate = 15;
    514 
    515   navigator.getUserMedia(constraints, okCallback,
    516                          failedCallback(constraintsTest));
    517 });
    518 
    519 // 10.4 NavigatorUserMediaSuccessCallback.
    520 var successCallbackTest =
    521   async_test('10.4 NavigatorUserMediaSuccessCallback');
    522 successCallbackTest.step(function() {
    523   var okCallback = successCallbackTest.step_func(function (stream) {
    524     assert_true(stream !== null);
    525     successCallbackTest.done();
    526   });
    527   invokeGetUserMedia(successCallbackTest, okCallback);
    528 });
    529 
    530 // 11. Constrainable Pattern.
    531 // TODO(phoglund): add tests.
    532