<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
    <head>
        <script src="../../resources/js-test-pre.js"></script>
        <script>
            var mediaStream;
            var videoTrack;
            var videoTrack2;
            var audioTrack;
            
            function error()
            {
                testFailed('Stream generation failed.');
                finishJSTest();
            }

            function getUserMedia(constraints, callback)
            {
                try {
                    navigator.webkitGetUserMedia(constraints, callback, error);
                } catch (e) {
                    testFailed('webkitGetUserMedia threw exception :' + e);
                    finishJSTest();
                }
            }

            function onTrackEnded()
            {
                testPassed('Track onended callback succeeded.');
                shouldBe('videoTrack', 'event.target');
                shouldBeEqualToString('videoTrack.readyState', 'ended');
                shouldBeEqualToString('videoTrack2.readyState', 'ended');
                debug("");
                finishJSTest();
            }

            function onTrackStarted()
            {
                testPassed('Track onstarted callback succeeded.');
                shouldBeEqualToString('videoTrack.readyState', 'live');
            }

            function onTrackUnmute()
            {
                testPassed('Track onunmute callback succeeded.');
                shouldBeEqualToString('videoTrack.readyState', 'live');
                videoTrack.stop();
            }

            function onTrackMute()
            {
                testPassed('Track onmute callback succeeded.');
                shouldBeEqual('videoTrack.muted', true);
            }
            
            function limitPrecision(value, precision)
            {
                if (typeof value === "number" && value % 1 != 0)
                    return value.toFixed(precision);
                return value;
            }

            function logCapabilityRange(name, range)
            {
                debug("  " + name);
                debug("   min : " + limitPrecision(range.min, 2));
                debug("   max : " + limitPrecision(range.max, 2));
                debug("   supported : " + range.supported);
            }

            function logCapabilityList(name, list)
            {
                debug("  " + name);
                for (i = 0; i < list.length; i++)
                    debug("    " + i + " : " + list[i]);
            }

            function checkCapabilities(track)
            {
                debug("checking track.getCapabilities()");
                capabilities = track.getCapabilities();
                logCapabilityList("sourceId", capabilities.sourceId);
                if (track.kind == "video") {
                    logCapabilityList("sourceType", capabilities.sourceType);
                    logCapabilityRange("width", capabilities.width);
                    logCapabilityRange("height", capabilities.height);
                    logCapabilityRange("frameRate", capabilities.frameRate);
                    logCapabilityRange("aspectRatio", capabilities.aspectRatio);
                    logCapabilityList("facingMode", capabilities.facingMode);
                } else
                    logCapabilityRange("volume", capabilities.volume);
            }

            function checkStates(track)
            {
                debug("checking track.states()");
                capabilities = track.getCapabilities();
                states = track.states();
                shouldBe('states.sourceId', 'capabilities.sourceId[0]');
                if (track.kind == "video")
                    shouldBeEqualToString('states.sourceType', 'camera');
                else
                    shouldBeEqualToString('states.sourceType', 'microphone');
                for (var property in states) {
                    // When https://webkit.org/b/129375 is fixed, we have to remove
                    // states.hasOwnProperty(property), because all the attributes
                    // will be in the prototype chain.
                    if (states.hasOwnProperty(property) || states.__proto__.hasOwnProperty(property))
                        debug("  states." + property + " = " + limitPrecision(states[property], 2));
                }
            }
            
            function checkTrack(t)
            {
                track = t;
                shouldBeEqualToString('track.readyState', 'live');
                checkCapabilities(track);
                checkStates(track);
                shouldNotThrow('track.getConstraints();');
            }

            function gotStream(stream)
            {
                mediaStream = stream;
                testPassed('getUserMedia succeeded.');
            
                debug("");
                evalAndLog("videoTrack = mediaStream.getVideoTracks()[0]");
                checkTrack(videoTrack);

                debug("");
                evalAndLog("audioTrack = mediaStream.getAudioTracks()[0]");
                checkTrack(audioTrack);

                videoTrack.onmute = onTrackMute;
                videoTrack.onunmute = onTrackUnmute;
                videoTrack.onended = onTrackEnded;
                videoTrack.onstarted = onTrackStarted;
            
                debug("");
                evalAndLog("videoTrack2 = videoTrack.clone()");
                videoTrack2.onended = onTrackEnded;
                checkTrack(videoTrack2);
                shouldNotBe('videoTrack.id', 'videoTrack2.id');
                shouldBe('videoTrack.kind', 'videoTrack2.kind');
                shouldBe('videoTrack.label', 'videoTrack2.label');
                shouldBe('videoTrack.muted', 'videoTrack2.muted');
                shouldBe('videoTrack.enabled', 'videoTrack2.enabled');
                shouldBe('videoTrack.readyState', 'videoTrack2.readyState');

                debug("");
                debug("track.stop() should not fire 'ended' on the track it is called on, but it should end all tracks using the same source");
                evalAndLog("videoTrack2.stop()");
            }

            function start()
            {
                description("Tests MediaStreamTrack callbacks.");
                
                // Note that the below behaviour doesn't reflect how it works outside of LayoutTests.
                // The underlying mock is modified to trigger the events when certain functions are called.
                // This modified behaviour allows us to test the MediaStreamTrack class properly.
                getUserMedia({audio:true, video:true}, gotStream);
            }

            window.jsTestIsAsync = true;
            window.successfullyParsed = true;
        </script>
    </head>
    <body onload="start()">
        <p id="description"></p>
        <div id="console"></div>
        <script src="../../resources/js-test-post.js"></script>
    </body>
</html>
