Updated W3C getusermedia tests to the latest version of the spec.

BUG=webrtc:3455
R=kjellander@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/16719004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@6459 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/webrtc/test/w3c/getusermedia_conformance_test.html b/webrtc/test/w3c/getusermedia_conformance_test.html
index 6ddb603..7b724a9 100644
--- a/webrtc/test/w3c/getusermedia_conformance_test.html
+++ b/webrtc/test/w3c/getusermedia_conformance_test.html
@@ -52,15 +52,16 @@
   </p>
 
   <div id="log"></div>
-  <video id="local-view" autoplay="autoplay" muted="true"></video>
-
   <!-- These files are in place when executing on W3C. -->
   <script src="/resources/testharness.js"></script>
   <script src="/resources/testharnessreport.js"></script>
   <script src="/common/vendor-prefix.js"
-          data-prefixed-objects=   '[{"ancestors":["navigator"], "name":"getUserMedia"},
-                                     {"ancestors":["window"], "name":"RTCPeerConnection"}]'
-          data-prefixed-prototypes='[{"ancestors":["HTMLMediaElement"],"name":"srcObject"}]'></script>
+          data-prefixed-objects=
+              '[{"ancestors":["navigator"], "name":"getUserMedia"},
+                {"ancestors":["window"], "name":"RTCPeerConnection"}]'
+          data-prefixed-prototypes=
+              '[{"ancestors":["HTMLMediaElement"],"name":"srcObject"}]'>
+  </script>
 
   <script src="getusermedia_conformance_test.js"></script>
 </body>
diff --git a/webrtc/test/w3c/getusermedia_conformance_test.js b/webrtc/test/w3c/getusermedia_conformance_test.js
index fe1dcc8..d7c7828 100644
--- a/webrtc/test/w3c/getusermedia_conformance_test.js
+++ b/webrtc/test/w3c/getusermedia_conformance_test.js
@@ -6,7 +6,7 @@
 // in the file PATENTS.  All contributing project authors may
 // be found in the AUTHORS file in the root of the source tree.
 
-setup({timeout:10000});
+setup({timeout:5000});
 
 // Helper functions to minimize code duplication.
 function failedCallback(test) {
@@ -19,6 +19,14 @@
       failedCallback(test));
 }
 
+function createInvisibleVideoTag() {
+  var video = document.createElement('video');
+  video.autoplay = true;
+  video.style.display = 'none';
+  document.body.appendChild(video);
+  return video;
+}
+
 // 4.2 MediaStream.
 var mediaStreamTest = async_test('4.2 MediaStream');
 
@@ -56,21 +64,25 @@
   }, '[MediaStream] removeTrack function');
 
   test(function () {
-    // Missing in Chrome.
     assert_inherits(stream, 'clone');
     assert_true(typeof stream.clone === 'function');
   }, '[MediaStream] clone function');
 
   test(function () {
-    assert_own_property(stream, 'ended');
-    assert_true(typeof stream.ended === 'boolean');
-    assert_readonly(stream, 'ended');
-  }, '[MediaStream] ended attribute');
+    assert_own_property(stream, 'active');
+    assert_true(typeof stream.active === 'boolean');
+    assert_readonly(stream, 'active');
+  }, '[MediaStream] active attribute');
 
   test(function () {
-    assert_own_property(stream, 'onended');
-    assert_true(stream.onended === null);
-  }, '[MediaStream] onended EventHandler');
+    assert_own_property(stream, 'onactive');
+    assert_true(stream.onactive === null);
+  }, '[MediaStream] onactive EventHandler');
+
+  test(function () {
+    assert_own_property(stream, 'oninactive');
+    assert_true(stream.oninactive === null);
+  }, '[MediaStream] oninactive EventHandler');
 
   test(function () {
     assert_own_property(stream, 'onaddtrack');
@@ -86,28 +98,43 @@
 mediaStreamTest.step(function() {
   var okCallback = mediaStreamTest.step_func(function (stream) {
     verifyMediaStream(stream);
-
     var videoTracks = stream.getVideoTracks();
     assert_true(videoTracks.length > 0);
+    mediaStreamTest.done();
+  });
 
-    // Verify event handlers are working.
-    stream.onaddtrack = onAddTrackCallback
-    stream.onremovetrack = onRemoveTrackCallback
-    stream.removeTrack(videoTracks[0]);
-    stream.addTrack(videoTracks[0]);
-    mediaStreamTest.done();
-  });
-  var onAddTrackCallback = mediaStreamTest.step_func(function () {
-    // TODO(kjellander): verify number of tracks.
-    mediaStreamTest.done();
-  });
-  var onRemoveTrackCallback = mediaStreamTest.step_func(function () {
-    // TODO(kjellander): verify number of tracks.
-    mediaStreamTest.done();
-  });
   invokeGetUserMedia(mediaStreamTest, okCallback);
 });
 
+var mediaStreamCallbacksTest = async_test('4.2.2 MediaStream callbacks');
+
+mediaStreamCallbacksTest.step(function() {
+  var addCallbackCalled = false;
+  var onAddTrackCallback = mediaStreamCallbacksTest.step_func(function (event) {
+    assert_true(event.track instanceof MediaStreamTrack);
+    addCallbackCalled = true;
+  });
+  var onRemoveTrackCallback =
+      mediaStreamCallbacksTest.step_func(function (event) {
+    assert_true(event.track instanceof MediaStreamTrack);
+    assert_true(addCallbackCalled, 'Add should have been called after remove.');
+    mediaStreamCallbacksTest.done();
+  });
+  var okCallback = mediaStreamCallbacksTest.step_func(function (stream) {
+    var videoTracks = stream.getVideoTracks();
+
+    // Verify event handlers are working.
+    stream.onaddtrack = onAddTrackCallback;
+    stream.onremovetrack = onRemoveTrackCallback;
+    stream.removeTrack(videoTracks[0]);
+    stream.addTrack(videoTracks[0]);
+  });
+
+  invokeGetUserMedia(mediaStreamCallbacksTest, okCallback);
+});
+
+// TODO(phoglund): add test for onactive/oninactive.
+
 // 4.3 MediaStreamTrack.
 var mediaStreamTrackTest = async_test('4.3 MediaStreamTrack');
 
@@ -140,7 +167,6 @@
   }, '[MediaStreamTrack (' + type + ')] enabled attribute');
 
   test(function () {
-    // Missing in Chrome.
     assert_own_property(track, 'muted');
     assert_readonly(track, 'muted');
     assert_true(typeof track.muted === 'boolean');
@@ -250,59 +276,113 @@
 var mediaStreamTrackEventTest = async_test('4.4 MediaStreamTrackEvent');
 mediaStreamTrackEventTest.step(function() {
   var okCallback = mediaStreamTrackEventTest.step_func(function (stream) {
-    // TODO(kjellander): verify attributes
+    // TODO(kjellander): verify attributes.
     mediaStreamTrackEventTest.done();
   });
   invokeGetUserMedia(mediaStreamTrackEventTest, okCallback);
 });
 
-// 4.5 Video and Audio Tracks tests.
-var avTracksTest = async_test('4.5 Video and Audio Tracks');
-avTracksTest.step(function() {
-  var okCallback = avTracksTest.step_func(function (stream) {
-    // TODO(kjellander): verify attributes
-    avTracksTest.done();
+// 6. Media streams as media elements.
+
+var playingInMediaElementTest = async_test(
+    '6.2 Loading and Playing a MediaStream in a Media Element');
+playingInMediaElementTest.step(function() {
+  var video = createInvisibleVideoTag();
+
+  var okCallback = playingInMediaElementTest.step_func(function (stream) {
+    video.onplay = playingInMediaElementTest.step_func(function() {
+      // This depends on what webcam we're actually running with, but the
+      // resolution should at least be greater than or equal to QVGA.
+      assert_greater_than_equal(video.videoWidth, 320);
+      assert_greater_than_equal(video.videoHeight, 240);
+
+      playingInMediaElementTest.done();
+    });
+    video.srcObject = stream;
   });
-  invokeGetUserMedia(avTracksTest, okCallback);
+  invokeGetUserMedia(playingInMediaElementTest, okCallback);
 });
 
-// 5. The model: sources, sinks, constraints, and states
+// Verifies a media element track (for instance belonging to a video tag)
+// after it has been assigned a media stream.
+function verifyOneMediaElementTrack(track, correspondingMediaStreamTrack) {
+  assert_equals(track.id, correspondingMediaStreamTrack.id);
+  assert_equals(track.kind, 'main');
+  assert_equals(track.label, correspondingMediaStreamTrack.label);
+  assert_equals(track.language, '');
+}
 
-// 6. Source states
-// 6.1 Dictionary MediaSourceStates Members
+var setsUpMediaTracksRightTest = async_test(
+    '6.2 Sets up <video> audio and video tracks right');
+setsUpMediaTracksRightTest.step(function() {
+  var video = createInvisibleVideoTag();
 
-// 7. Source capabilities
-// 7.1 Dictionary CapabilityRange Members
-// 7.2 CapabilityList array
-// 7.3 Dictionary AllVideoCapabilities Members
-// 7.4 Dictionary AllAudioCapabilities Members
+  var okCallback = setsUpMediaTracksRightTest.step_func(function (stream) {
+    video.onplay = setsUpMediaTracksRightTest.step_func(function() {
+      // Verify the requirements on the video tag's streams as outlined in 6.2.
+      // There could be any number of tracks depending on what device we have
+      // connected, so verify all of them. There should be at least one of audio
+      // and video each though.
+      assert_inherits(video, 'videoTracks',
+                      'Browser missing videoTracks support on media elements.');
+      assert_readonly(video, 'videoTracks');
+      assert_greater_than_equal(video.videoTracks.length, 1);
+      assert_equals(video.videoTracks.length, stream.getVideoTracks().length);
 
-// 8. URL tests.
-var createObjectURLTest = async_test('8.1 URL createObjectURL method');
-createObjectURLTest.step(function() {
-  var okCallback = createObjectURLTest.step_func(function (stream) {
-    var url = URL.createObjectURL(stream);
-    assert_true(typeof url === 'string');
-    createObjectURLTest.done();
+      for (var i = 0; i < video.videoTracks.length; i++) {
+        verifyOneMediaElementTrack(video.videoTracks[i],
+                                   stream.getVideoTracks()[i]);
+      }
+
+      assert_inherits(video, 'audioTracks',
+                      'Browser missing audioTracks support on media elements.');
+      assert_readonly(video, 'audioTracks');
+      assert_greater_than_equal(video.audioTracks.length, 1);
+      assert_equals(video.audioTracks.length, stream.getAudioTracks().length);
+
+      for (var i = 0; i < video.audioTracks.length; i++) {
+        verifyOneMediaElementTrack(audio.audioTracks[i],
+                                   stream.getAudioTracks()[i]);
+      }
+
+      setsUpMediaTracksRightTest.done();
+    });
+    video.srcObject = stream;
   });
-  invokeGetUserMedia(createObjectURLTest, okCallback);
+  invokeGetUserMedia(setsUpMediaTracksRightTest, okCallback);
 });
 
-// 9. MediaStreams as Media Elements.
-var mediaElementsTest = async_test('9. MediaStreams as Media Elements');
+var mediaElementsTest =
+  async_test('6.3 Media Element Attributes when Playing a MediaStream');
 
 function verifyVideoTagWithStream(videoTag) {
   test(function () {
+    assert_equals(videoTag.currentSrc, '');
+  }, '[Video tag] currentSrc attribute');
+
+  test(function () {
+    assert_equals(videoTag.preload, 'none');
+  }, '[Video tag] preload attribute');
+
+  test(function () {
     assert_equals(videoTag.buffered.length, 0);
   }, '[Video tag] buffered attribute');
 
   test(function () {
-    // Attempts to alter currentTime shall be ignored.
+    // Where 1 is NETWORK_IDLE.
+    assert_equals(videoTag.networkState, 1);
+  }, '[Video tag] networkState attribute');
+
+  test(function () {
+    // 0 is HAVE_NOTHING, 4 is HAVE_ENOUGH_DATA.
+    assert_true(videoTag.readyState == 0 || videoTag.readyState == 4);
+  }, '[Video tag] readyState attribute');
+
+  test(function () {
     assert_true(videoTag.currentTime >= 0);
-    assert_throws('InvalidStateError',
-                  function () { videoTag.currentTime = 1234; },
-                  'Attempts to modify currentTime shall throw ' +
-                      'InvalidStateError');
+    assert_throws(
+        'InvalidStateError', function () { videoTag.currentTime = 1234; },
+        'Attempts to modify currentTime shall throw InvalidStateError');
   }, '[Video tag] currentTime attribute');
 
   test(function () {
@@ -315,16 +395,16 @@
 
   test(function () {
     assert_equals(videoTag.defaultPlaybackRate, 1.0);
-    assert_throws('DOMException',
-                  function () { videoTag.defaultPlaybackRate = 2.0; },
-                  'Attempts to alter videoTag.defaultPlaybackRate MUST fail');
+    assert_throws(
+        'DOMException', function () { videoTag.defaultPlaybackRate = 2.0; },
+        'Attempts to alter videoTag.defaultPlaybackRate MUST fail');
   }, '[Video tag] defaultPlaybackRate attribute');
 
   test(function () {
     assert_equals(videoTag.playbackRate, 1.0);
-    assert_throws('DOMException',
-      function () { videoTag.playbackRate = 2.0; },
-      'Attempts to alter videoTag.playbackRate MUST fail');
+    assert_throws(
+        'DOMException', function () { videoTag.playbackRate = 2.0; },
+        'Attempts to alter videoTag.playbackRate MUST fail');
   }, '[Video tag] playbackRate attribute');
 
   test(function () {
@@ -335,56 +415,96 @@
 
   test(function () {
     assert_equals(videoTag.seekable.length, 0);
-    assert_equals(videoTag.seekable.start(), videoTag.currentTime);
-    assert_equals(videoTag.seekable.end(), videoTag.currentTime);
-    assert_equals(videoTag.startDate, NaN, 'videoTag.startDate');
+    // This is wrong in the standard: start() and end() must have arguments, but
+    // since the time range is empty as we assert in the line above, there is no
+    // valid argument with which we can call the methods.
+    // assert_equals(videoTag.seekable.start(), videoTag.currentTime);
+    // assert_equals(videoTag.seekable.end(), videoTag.currentTime);
   }, '[Video tag] seekable attribute');
 
   test(function () {
+    assert_equals(videoTag.startDate, NaN, 'videoTag.startDate');
+  }, '[Video tag] startDate attribute');
+
+  test(function () {
     assert_false(videoTag.loop);
   }, '[Video tag] loop attribute');
+
 };
 
 mediaElementsTest.step(function() {
   var okCallback = mediaElementsTest.step_func(function (stream) {
-    var videoTag = document.getElementById('local-view');
-    videoTag.srcObject = stream;
-    verifyVideoTagWithStream(videoTag);
+    var video = createInvisibleVideoTag();
+    video.srcObject = stream;
+    verifyVideoTagWithStream(video);
     mediaElementsTest.done();
   });
   invokeGetUserMedia(mediaElementsTest, okCallback);
 });
 
-// 11. Obtaining local multimedia content.
+// 9. Enumerating local media devices.
+// TODO(phoglund): add tests.
 
-// 11.1 NavigatorUserMedia.
-var getUserMediaTest = async_test('11.1 NavigatorUserMedia');
-getUserMediaTest.step(function() {
-  var okCallback = getUserMediaTest.step_func(function (stream) {
+// 10. Obtaining local multimedia content.
+
+function testGetUserMedia(test, constraints) {
+  var okCallback = test.step_func(function (stream) {
     assert_true(stream !== null);
-    getUserMediaTest.done();
+    test.done();
   });
+  navigator.getUserMedia(constraints, okCallback, failedCallback(test));
+}
 
-  // All three arguments are mandatory, so pass all of them.
-  navigator.getUserMedia({ video: true, audio: true }, okCallback,
-      failedCallback(getUserMediaTest));
-  navigator.getUserMedia({ video: true, audio: false }, okCallback,
-      failedCallback(getUserMediaTest));
-  navigator.getUserMedia({ video: false, audio: true }, okCallback,
-      failedCallback(getUserMediaTest));
+var getUserMediaTestAudioVideo = async_test('10.1.1 NavigatorUserMedia A/V');
+getUserMediaTestAudioVideo.step(function() {
+  testGetUserMedia(getUserMediaTestAudioVideo, { video: true, audio: true });
 });
 
-// 11.2 MediaStreamConstraints.
-var constraintsTest = async_test('11.2 MediaStreamConstraints');
+var getUserMediaTestVideo = async_test('10.1.1 NavigatorUserMedia V');
+getUserMediaTestVideo.step(function() {
+  testGetUserMedia(getUserMediaTestVideo, { video: true, audio: false });
+});
+
+var getUserMediaTestAudio = async_test('10.1.1 NavigatorUserMedia A');
+getUserMediaTestAudio.step(function() {
+  testGetUserMedia(getUserMediaTestAudio, { video: false, audio: true });
+});
+
+var getUserMediaTestNull = async_test('10.1.1 NavigatorUserMedia Null');
+getUserMediaTestNull.step(function() {
+  testGetUserMedia(getUserMediaTestNull, null);
+});
+
+var getUserMediaTestPeerIdentity =
+    async_test('10.2 NavigatorUserMedia with peerIdentity');
+getUserMediaTestPeerIdentity.step(function() {
+  var peerIdentity = 'my_identity';
+  var okCallback = getUserMediaTestPeerIdentity.step_func(function (stream) {
+    assert_true(stream !== null);
+    stream.getVideoTracks().forEach(function(track) {
+      assert_equals(track.peerIdentity, peerIdentity);
+    });
+    stream.getAudioTracks().forEach(function(track) {
+      assert_equals(track.peerIdentity, peerIdentity);
+    });
+    getUserMediaTestPeerIdentity.done();
+  });
+  navigator.getUserMedia(
+      {video: true, audio: true, peerIdentity: 'my_identity' },
+      okCallback, failedCallback(getUserMediaTestPeerIdentity));
+});
+
+// 10.2 MediaStreamConstraints.
+var constraintsTest = async_test('10.2 MediaStreamConstraints');
 constraintsTest.step(function() {
   var okCallback = constraintsTest.step_func(function (stream) {
     assert_true(stream !== null);
     constraintsTest.done();
   });
 
-  // Constraints on video.
-  // See http://webrtc.googlecode.com/svn/trunk/samples/js/demos/html/constraints-and-stats.html
+  // See https://googlechrome.github.io/webrtc/samples/web/content/constraints/
   // for more examples of constraints.
+  // TODO(phoglund): test more constraints; the possibilities here are endless.
   var constraints = {};
   constraints.audio = true;
   constraints.video = { mandatory: {}, optional: [] };
@@ -396,9 +516,9 @@
                          failedCallback(constraintsTest));
 });
 
-// 11.3 NavigatorUserMediaSuccessCallback.
+// 10.4 NavigatorUserMediaSuccessCallback.
 var successCallbackTest =
-  async_test('11.3 NavigatorUserMediaSuccessCallback');
+  async_test('10.4 NavigatorUserMediaSuccessCallback');
 successCallbackTest.step(function() {
   var okCallback = successCallbackTest.step_func(function (stream) {
     assert_true(stream !== null);
@@ -407,3 +527,5 @@
   invokeGetUserMedia(successCallbackTest, okCallback);
 });
 
+// 11. Constrainable Pattern.
+// TODO(phoglund): add tests.