|  | <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> | 
|  | <html> | 
|  | <head> | 
|  | <title>GetUserMedia Browser Conformance Test</title> | 
|  | <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> | 
|  | <meta charset="utf-8"> | 
|  | </head> | 
|  |  | 
|  | <!-- | 
|  | To quickly iterate when developing this test, make sure you select | 
|  | 'Always allow this site to use this webcam' option in the dropdown menu of | 
|  | Chrome when it's requesting access to your webcam. | 
|  | Notice that this requires the site you're browsing to use HTTPS. | 
|  | --> | 
|  |  | 
|  | <body> | 
|  | <h1>Conformance test for the Media Capture and Streams API</h1> | 
|  | <p>This page contains a foundation of conformance tests that can be expanded | 
|  | to cover most things in the W3C specification of the Media Capture and Streams | 
|  | API.</p> | 
|  | <p>VERSION: These tests are based on the W3C Editor's Draft of 25 September | 
|  | 2012. | 
|  | <p>STATUS: In its current state, it only performs simple checks on the various | 
|  | attributes and methods of the objects exposed by the API. There's not much | 
|  | functionality tested so far.</p> | 
|  | <p>PREREQUISITES: You must have a webcam available on the machine that the | 
|  | test is executed at.</p> | 
|  | <p>PREFIX: These tests currently utilizes the <pre>webkit</pre> prefix, so | 
|  | that will have to changed in order to to test conformance with the actual | 
|  | standard!</p> | 
|  | <p>SPEC: <a href="http://dev.w3.org/2011/webrtc/editor/getusermedia.html"> | 
|  | http://dev.w3.org/2011/webrtc/editor/getusermedia.html</a></p> | 
|  |  | 
|  | <div id="log"></div> | 
|  | <script src="https://w3c-test.org/resources/testharness.js"></script> | 
|  |  | 
|  | <script type="text/javascript"> | 
|  | setup({timeout:10000}); | 
|  |  | 
|  | // Helper functions to minimize code duplication. | 
|  | function failedCallback(test) { | 
|  | return test.step_func(function (error) { | 
|  | assert_unreached('Should not get an error callback'); | 
|  | }); | 
|  | } | 
|  | function invokeGetUserMedia(test, okCallback) { | 
|  | navigator.webkitGetUserMedia({ video: true, audio: true }, okCallback, | 
|  | failedCallback(test)); | 
|  | } | 
|  |  | 
|  | // MediaStream. | 
|  | var mediaStreamTest = async_test('4.2 MediaStream'); | 
|  | function verifyMediaStream(stream) { | 
|  | test(function () { | 
|  | assert_own_property(stream, 'label'); | 
|  | assert_true(typeof stream.label === 'string'); | 
|  | assert_readonly(stream, 'label'); | 
|  | }, "MediaStream label attribute"); | 
|  |  | 
|  | test(function () { | 
|  | assert_own_property(stream, 'audioTracks'); | 
|  | assert_true(typeof stream.audioTracks === 'object', | 
|  | 'audioTracks is a MediaStreamTrackList'); | 
|  | assert_readonly(stream, 'audioTracks'); | 
|  | }, "MediaStream audioTracks attribute"); | 
|  |  | 
|  | test(function () { | 
|  | assert_own_property(stream, 'videoTracks'); | 
|  | assert_true(typeof stream.videoTracks === 'object', | 
|  | 'videoTracks is a MediaStreamTrackList'); | 
|  | assert_readonly(stream, 'videoTracks'); | 
|  | }, "MediaStream videoTracks attribute"); | 
|  |  | 
|  | test(function () { | 
|  | assert_own_property(stream, 'ended'); | 
|  | assert_true(typeof stream.ended === 'boolean'); | 
|  | assert_false(stream.ended); | 
|  | }, "MediaStream ended attribute"); | 
|  |  | 
|  | test(function () { | 
|  | assert_own_property(stream, 'onended'); | 
|  | assert_true(stream.onended === null); | 
|  | }, "MediaStream onended EventHandler"); | 
|  | } | 
|  | mediaStreamTest.step(function() { | 
|  | var okCallback = mediaStreamTest.step_func(function (localStream) { | 
|  | verifyMediaStream(localStream); | 
|  |  | 
|  | // Verify event handlers are working. | 
|  | localStream.onended = onendedCallback | 
|  | assert_false(localStream.ended); | 
|  | localStream.stop(); | 
|  | }); | 
|  | var onendedCallback = mediaStreamTest.step_func(function () { | 
|  | assert_true(localStream.ended); | 
|  | mediaStreamTest.done(); | 
|  | }); | 
|  | invokeGetUserMedia(mediaStreamTest, okCallback);; | 
|  | }); | 
|  |  | 
|  | // LocalMediaStream. | 
|  | var localMediaStreamTest = async_test('4.3 LocalMediaStream'); | 
|  | localMediaStreamTest.step(function() { | 
|  | var okCallback = localMediaStreamTest.step_func(function (localStream) { | 
|  | assert_own_property(localStream, 'stop'); | 
|  | assert_true(typeof localStream.stop === 'function'); | 
|  | localMediaStreamTest.done(); | 
|  | }); | 
|  | invokeGetUserMedia(localMediaStreamTest, okCallback); | 
|  | }); | 
|  |  | 
|  | // MediaStreamTrack. | 
|  | var mediaStreamTrackTest = async_test('4.4 MediaStreamTrack'); | 
|  | function verifyTrack(type, track) { | 
|  | test(function () { | 
|  | assert_own_property(track, 'kind'); | 
|  | assert_readonly(track, 'kind'); | 
|  | }, 'MediaStreamTrack (' + type + ') kind attribute'); | 
|  |  | 
|  | test(function () { | 
|  | assert_own_property(track, 'label'); | 
|  | assert_true(typeof track.label === 'string', | 
|  | 'label is an object (DOMString)'); | 
|  | assert_readonly(track, 'label'); | 
|  | }, 'MediaStreamTrack (' + type + ') label attribute'); | 
|  |  | 
|  | test(function () { | 
|  | assert_own_property(track, 'enabled'); | 
|  | assert_true(typeof track.enabled === 'boolean', | 
|  | 'enabled is an object (DOMString)'); | 
|  | assert_true(track.enabled, 'enabled property must be true initially'); | 
|  | }, 'MediaStreamTrack (' + type + ') enabled attribute'); | 
|  |  | 
|  | test(function () { | 
|  | assert_own_property(track, 'readyState'); | 
|  | assert_true(typeof track.readyState === 'number'); | 
|  | assert_readonly(track, 'readyState'); | 
|  | assert_equals(track.readyState, track.LIVE, | 
|  | 'readyState must be LIVE initially'); | 
|  | }, 'MediaStreamTrack (' + type + ') readyState attribute'); | 
|  |  | 
|  | test(function () { | 
|  | assert_own_property(track, 'onmute'); | 
|  | assert_true(track.onmute === null); | 
|  | }, 'MediaStreamTrack (' + type + ') onmute EventHandler'); | 
|  |  | 
|  | test(function () { | 
|  | assert_own_property(track, 'onunmute'); | 
|  | assert_true(track.onunmute === null); | 
|  | }, 'MediaStreamTrack (' + type + ') onunmute EventHandler'); | 
|  |  | 
|  | test(function () { | 
|  | assert_own_property(track, 'onended'); | 
|  | assert_true(track.onended === null); | 
|  | }, 'MediaStreamTrack (' + type + ') onended EventHandler'); | 
|  | } | 
|  | mediaStreamTrackTest.step(function() { | 
|  | var okCallback = mediaStreamTrackTest.step_func(function (localStream) { | 
|  | verifyTrack('audio', localStream.audioTracks[0]); | 
|  | verifyTrack('video', localStream.videoTracks[0]); | 
|  | mediaStreamTrackTest.done(); | 
|  | }); | 
|  | invokeGetUserMedia(mediaStreamTrackTest, okCallback); | 
|  | }); | 
|  |  | 
|  | // URL tests. | 
|  | var createObjectURLTest = async_test('4.5 URL createObjectURL method'); | 
|  | createObjectURLTest.step(function() { | 
|  | var okCallback = createObjectURLTest.step_func(function (localStream) { | 
|  | var url = webkitURL.createObjectURL(localStream); | 
|  | assert_true(typeof url === 'string'); | 
|  | createObjectURLTest.done(); | 
|  | }); | 
|  | invokeGetUserMedia(createObjectURLTest, okCallback); | 
|  | }); | 
|  |  | 
|  | // MediaStreamTrackList tests. | 
|  | var mediaStreamTrackListTest = async_test('4.6 MediaStreamTrackList'); | 
|  | function verifyTrackList(type, trackList) { | 
|  | test(function () { | 
|  | assert_own_property(trackList, 'length'); | 
|  | assert_true(typeof trackList.length === 'number'); | 
|  | assert_true(trackList.length === 1); | 
|  | }, 'MediaStreamTrackList (' + type + ') length attribute'); | 
|  |  | 
|  | test(function () { | 
|  | assert_own_property(trackList, 'item'); | 
|  | assert_true(typeof trackList.item === 'function'); | 
|  | assert_true(trackList.item(0) !== null); | 
|  | }, 'MediaStreamTrackList (' + type + ') item() method'); | 
|  |  | 
|  | test(function () { | 
|  | assert_own_property(trackList, 'add'); | 
|  | assert_true(typeof trackList.add === 'function'); | 
|  | }, 'MediaStreamTrackList (' + type + ') add() method'); | 
|  |  | 
|  | test(function () { | 
|  | assert_own_property(trackList, 'remove'); | 
|  | assert_true(typeof trackList.remove === 'function'); | 
|  | }, 'MediaStreamTrackList (' + type + ') remove() method'); | 
|  |  | 
|  | test(function () { | 
|  | assert_own_property(trackList, 'onaddtrack'); | 
|  | assert_true(trackList.onaddtrack === null); | 
|  | }, 'MediaStreamTrackList (' + type + ') onaddtrack EventHandler'); | 
|  |  | 
|  | test(function () { | 
|  | assert_own_property(trackList, 'onremovetrack'); | 
|  | assert_true(trackList.onremovetrack === null); | 
|  | }, 'MediaStreamTrackList (' + type + ') onremovetrack EventHandler'); | 
|  | } | 
|  | mediaStreamTrackListTest.step(function() { | 
|  | var okCallback = mediaStreamTrackListTest.step_func(function (localStream) { | 
|  | verifyTrackList('audioTracks', localStream.audioTracks); | 
|  | verifyTrackList('videoTracks', localStream.videoTracks); | 
|  | mediaStreamTrackListTest.done(); | 
|  | }); | 
|  | invokeGetUserMedia(mediaStreamTrackListTest, okCallback); | 
|  | }); | 
|  |  | 
|  | // MediaStreams as Media Elements. | 
|  | var mediaElementsTest = async_test('4.7 MediaStreams as Media Elements'); | 
|  | mediaElementsTest.step(function() { | 
|  | var okCallback = mediaElementsTest.step_func(function (localStream) { | 
|  | var url = webkitURL.createObjectURL(localStream); | 
|  | var videoTag = document.getElementById('local-view'); | 
|  | videoTag.src = url; | 
|  | assert_equals(videoTag.currentSrc, url); | 
|  | assert_false(videoTag.preload); | 
|  | assert_equals(videoTag.buffered.length, 1); | 
|  | assert_equals(videoTag.buffered.start(0), 0); | 
|  | assert_equals(videoTag.buffered.end(0), 0); | 
|  | // Attempts to alter currentTime shall be ignored. | 
|  | assert_true(videoTag.currentTime >= 0); | 
|  | var time = videoTag.currentTime; | 
|  | videoTag.currentTime = time - 100; | 
|  | assert_true(videoTag.currentTime >= time); | 
|  |  | 
|  | assert_equals(videoTag.duration, infinity); | 
|  | assert_false(videoTag.seeking); | 
|  | assert_equals(videoTag.defaultPlaybackRate, 1.0); | 
|  | // Attempts to alter defaultPlaybackRate shall fail. | 
|  | assert_throws(videoTag.defaultPlaybackRate = 2.0); | 
|  |  | 
|  | assert_equals(videoTag.playbackRate, 1.0); | 
|  | // Attempts to alter playbackRate shall fail. | 
|  | assert_throws(videoTag.playbackRate = 2.0); | 
|  |  | 
|  | assert_equals(videoTag.played.length, 1); | 
|  | assert_equals(videoTag.played.start(0), 0); | 
|  | assert_true(videoTag.played.end(0) >= videoTag.currentTime); | 
|  | assert_equals(videoTag.seekable.length, 0); | 
|  | assert_equals(videoTag.seekable.start(), videoTag.currentTime); | 
|  | assert_equals(videoTag.seekable.end(), videoTag.currentTime); | 
|  | assert_equals(videoTag.startOffsetTime, NaN); | 
|  | assert_false(videoTag.loop); | 
|  | mediaElementsTest.done(); | 
|  | }); | 
|  | invokeGetUserMedia(mediaElementsTest, okCallback); | 
|  | }); | 
|  |  | 
|  | // NavigatorUserMedia. | 
|  | var getUserMediaTest = async_test('5.1 NavigatorUserMedia'); | 
|  | getUserMediaTest.step(function() { | 
|  | var okCallback = getUserMediaTest.step_func(function (localStream) { | 
|  | assert_true(localStream !== null); | 
|  | getUserMediaTest.done(); | 
|  | }); | 
|  |  | 
|  | // boolean parameters, without failure callback: | 
|  | navigator.webkitGetUserMedia({ video: true, audio: true }, okCallback); | 
|  | navigator.webkitGetUserMedia({ video: true, audio: false }, okCallback); | 
|  | navigator.webkitGetUserMedia({ video: false, audio: true }, okCallback); | 
|  |  | 
|  | // boolean parameters, with failure callback: | 
|  | navigator.webkitGetUserMedia({ video: true, audio: true }, okCallback, | 
|  | failedCallback(getUserMediaTest)); | 
|  | navigator.webkitGetUserMedia({ video: true, audio: false }, okCallback, | 
|  | failedCallback(getUserMediaTest)); | 
|  | navigator.webkitGetUserMedia({ video: false, audio: true }, okCallback, | 
|  | failedCallback(getUserMediaTest)); | 
|  | }); | 
|  |  | 
|  | // MediaStreamConstraints. | 
|  | var constraintsTest = | 
|  | async_test('5.2 MediaStreamConstraints'); | 
|  | constraintsTest.step(function() { | 
|  | var okCallback = constraintsTest.step_func(function (localStream) { | 
|  | assert_true(localStream !== null); | 
|  | constraintsTest.done(); | 
|  | }); | 
|  |  | 
|  | // Constraints on video. | 
|  | // See http://code.google.com/p/webrtc-samples/source/browse/trunk/demos/html/constraints-and-stats.html | 
|  | // for more examples of constraints. | 
|  | var constraints = {}; | 
|  | constraints.audio = true; | 
|  | constraints.video = { mandatory: {}, optional: [] }; | 
|  | constraints.video.mandatory.minWidth = 640; | 
|  | constraints.video.mandatory.minHeight = 480; | 
|  | constraints.video.mandatory.minFrameRate = 15; | 
|  |  | 
|  | navigator.webkitGetUserMedia(constraints, okCallback, | 
|  | failedCallback(constraintsTest)); | 
|  | }); | 
|  |  | 
|  | // NavigatorUserMediaSuccessCallback. | 
|  | var successCallbackTest = | 
|  | async_test('5.3 NavigatorUserMediaSuccessCallback'); | 
|  | successCallbackTest.step(function() { | 
|  | var okCallback = successCallbackTest.step_func(function (localStream) { | 
|  | assert_true(localStream !== null); | 
|  | assert_own_property(localStream, 'stop'); | 
|  | successCallbackTest.done(); | 
|  | }); | 
|  | invokeGetUserMedia(successCallbackTest, okCallback); | 
|  | }); | 
|  |  | 
|  | // NavigatorUserMediaError and NavigatorUserMediaErrorCallback. | 
|  | var errorCallbackTest = | 
|  | async_test('5.4 NavigatorUserMediaError and ' + | 
|  | 'NavigatorUserMediaErrorCallback'); | 
|  | errorCallbackTest.step(function() { | 
|  | var okCallback = errorCallbackTest.step_func(function (localStream) { | 
|  | assert_unreached('Should not get a success callback'); | 
|  | }); | 
|  | var errorCallback = errorCallbackTest.step_func(function (error) { | 
|  | assert_own_property(error, 'PERMISSION_DENIED'); | 
|  | assert_readonly(error.PERMISSION_DENIED); | 
|  | assert_true(typeof error.PERMISSION_DENIED === 'number'); | 
|  | assert_own_property(error, 'code'); | 
|  | assert_readonly(error.code); | 
|  | assert_true(typeof error.code === 'number'); | 
|  |  | 
|  | }); | 
|  | // Setting both audio and video to false triggers an error callback. | 
|  | // TODO(kjellander): Figure out if there's a way in the spec to trigger an | 
|  | // error callback. | 
|  | navigator.webkitGetUserMedia({ video: false, audio: false }, okCallback, | 
|  | errorCallback); | 
|  | }); | 
|  | </script> | 
|  | <video width="640" height="480" id="local-view" autoplay="autoplay"></video> | 
|  | </body> | 
|  | </html> |