--- a/dom/media/tests/mochitest/constraints.js +++ b/dom/media/tests/mochitest/constraints.js @@ -47,20 +47,28 @@ var common_tests = [ /** * Starts the test run by running through each constraint * test by verifying that the right resolution and rejection is fired. */ function testConstraints(tests) { - function testgum(prev, test) { - return prev.then(() => navigator.mediaDevices.getUserMedia(test.constraints)) - .then(() => is(null, test.error, test.message), - reason => is(reason.name, test.error, test.message + ": " + reason.message)); + function testgum(p, test) { + return p.then(function() { + return navigator.mediaDevices.getUserMedia(test.constraints); + }) + .then(function() { + is(null, test.error, test.message); + }, function(reason) { + is(reason.name, test.error, test.message + ": " + reason.message); + }); } - tests.reduce(testgum, Promise.resolve()) - .catch(reason => { - ok(false, "Unexpected failure: " + reason.message); - }) - .then(SimpleTest.finish); + var p = new Promise(function(resolve) { resolve(); }); + tests.forEach(function(test) { + p = testgum(p, test); + }); + p.catch(function(reason) { + ok(false, "Unexpected failure: " + reason.message); + }) + .then(SimpleTest.finish); }
--- a/dom/media/tests/mochitest/dataChannel.js +++ b/dom/media/tests/mochitest/dataChannel.js @@ -1,170 +1,230 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -/** - * Returns the contents of a blob as text - * - * @param {Blob} blob - The blob to retrieve the contents from - */ -function getBlobContent(blob) { - return new Promise(resolve => { - var reader = new FileReader(); - // Listen for 'onloadend' which will always be called after a success or failure - reader.onloadend = event => resolve(event.target.result); - reader.readAsText(blob); - }); -} - function addInitialDataChannel(chain) { chain.insertBefore('PC_LOCAL_CREATE_OFFER', [ - function PC_REMOTE_EXPECT_DATA_CHANNEL(test) { - test.pcRemote.expectDataChannel(); - }, + ['PC_LOCAL_CREATE_DATA_CHANNEL', + function (test) { + var channel = test.pcLocal.createDataChannel({}); - function PC_LOCAL_CREATE_DATA_CHANNEL(test) { - var channel = test.pcLocal.createDataChannel({}); - is(channel.binaryType, "blob", channel + " is of binary type 'blob'"); - is(channel.readyState, "connecting", channel + " is in state: 'connecting'"); + is(channel.binaryType, "blob", channel + " is of binary type 'blob'"); + is(channel.readyState, "connecting", channel + " is in state: 'connecting'"); + + is(test.pcLocal.signalingState, STABLE, + "Create datachannel does not change signaling state"); - is(test.pcLocal.signalingState, STABLE, - "Create datachannel does not change signaling state"); - } + test.next(); + } + ] ]); - + chain.insertAfter('PC_REMOTE_CREATE_ANSWER', [ + [ + 'PC_LOCAL_SETUP_DATA_CHANNEL_CALLBACK', + function (test) { + test.waitForInitialDataChannel(test.pcLocal, function () { + ok(true, test.pcLocal + " dataChannels[0] switched to 'open'"); + }, + // At this point a timeout failure will be of no value + null); + test.next(); + } + ], + [ + 'PC_REMOTE_SETUP_DATA_CHANNEL_CALLBACK', + function (test) { + test.waitForInitialDataChannel(test.pcRemote, function () { + ok(true, test.pcRemote + " dataChannels[0] switched to 'open'"); + }, + // At this point a timeout failure will be of no value + null); + test.next(); + } + ] + ]); chain.insertBefore('PC_LOCAL_CHECK_MEDIA_TRACKS', [ - function PC_LOCAL_VERIFY_DATA_CHANNEL_STATE(test) { - return test.pcLocal.dataChannels[0].opened; - }, - - function PC_REMOTE_VERIFY_DATA_CHANNEL_STATE(test) { - return test.pcRemote.nextDataChannel.then(channel => channel.opened); - } + [ + 'PC_LOCAL_VERIFY_DATA_CHANNEL_STATE', + function (test) { + test.waitForInitialDataChannel(test.pcLocal, function() { + test.next(); + }, function() { + ok(false, test.pcLocal + " initial dataChannels[0] failed to switch to 'open'"); + //TODO: use stopAndExit() once bug 1019323 has landed + unexpectedEventAndFinish(this, 'timeout') + // to prevent test framework timeouts + test.next(); + }); + } + ], + [ + 'PC_REMOTE_VERIFY_DATA_CHANNEL_STATE', + function (test) { + test.waitForInitialDataChannel(test.pcRemote, function() { + test.next(); + }, function() { + ok(false, test.pcRemote + " initial dataChannels[0] failed to switch to 'open'"); + //TODO: use stopAndExit() once bug 1019323 has landed + unexpectedEventAndFinish(this, 'timeout'); + // to prevent test framework timeouts + test.next(); + }); + } + ] ]); chain.removeAfter('PC_REMOTE_CHECK_ICE_CONNECTIONS'); chain.append([ - function SEND_MESSAGE(test) { - var message = "Lorem ipsum dolor sit amet"; + [ + 'SEND_MESSAGE', + function (test) { + var message = "Lorem ipsum dolor sit amet"; - return test.send(message).then(result => { - is(result.data, message, "Message correctly transmitted from pcLocal to pcRemote."); - }); - }, + test.send(message, function (channel, data) { + is(data, message, "Message correctly transmitted from pcLocal to pcRemote."); - function SEND_BLOB(test) { - var contents = ["At vero eos et accusam et justo duo dolores et ea rebum."]; - var blob = new Blob(contents, { "type" : "text/plain" }); + test.next(); + }); + } + ], + [ + 'SEND_BLOB', + function (test) { + var contents = ["At vero eos et accusam et justo duo dolores et ea rebum."]; + var blob = new Blob(contents, { "type" : "text/plain" }); - return test.send(blob).then(result => { - ok(result.data instanceof Blob, "Received data is of instance Blob"); - is(result.data.size, blob.size, "Received data has the correct size."); + test.send(blob, function (channel, data) { + ok(data instanceof Blob, "Received data is of instance Blob"); + is(data.size, blob.size, "Received data has the correct size."); - return getBlobContent(result.data); - }).then(recv_contents => - is(recv_contents, contents, "Received data has the correct content.")); - }, + getBlobContent(data, function (recv_contents) { + is(recv_contents, contents, "Received data has the correct content."); - function CREATE_SECOND_DATA_CHANNEL(test) { - return test.createDataChannel({ }).then(result => { - var sourceChannel = result.local; - var targetChannel = result.remote; - is(sourceChannel.readyState, "open", sourceChannel + " is in state: 'open'"); - is(targetChannel.readyState, "open", targetChannel + " is in state: 'open'"); - - is(targetChannel.binaryType, "blob", targetChannel + " is of binary type 'blob'"); - }); - }, + test.next(); + }); + }); + } + ], + [ + 'CREATE_SECOND_DATA_CHANNEL', + function (test) { + test.createDataChannel({ }, function (sourceChannel, targetChannel) { + is(sourceChannel.readyState, "open", sourceChannel + " is in state: 'open'"); + is(targetChannel.readyState, "open", targetChannel + " is in state: 'open'"); - function SEND_MESSAGE_THROUGH_LAST_OPENED_CHANNEL(test) { - var channels = test.pcRemote.dataChannels; - var message = "I am the Omega"; + is(targetChannel.binaryType, "blob", targetChannel + " is of binary type 'blob'"); + is(targetChannel.readyState, "open", targetChannel + " is in state: 'open'"); - return test.send(message).then(result => { - is(channels.indexOf(result.channel), channels.length - 1, "Last channel used"); - is(result.data, message, "Received message has the correct content."); - }); - }, + test.next(); + }); + } + ], + [ + 'SEND_MESSAGE_THROUGH_LAST_OPENED_CHANNEL', + function (test) { + var channels = test.pcRemote.dataChannels; + var message = "Lorem ipsum dolor sit amet"; + test.send(message, function (channel, data) { + is(channels.indexOf(channel), channels.length - 1, "Last channel used"); + is(data, message, "Received message has the correct content."); - function SEND_MESSAGE_THROUGH_FIRST_CHANNEL(test) { - var message = "Message through 1st channel"; - var options = { - sourceChannel: test.pcLocal.dataChannels[0], - targetChannel: test.pcRemote.dataChannels[0] - }; + test.next(); + }); + } + ], + [ + 'SEND_MESSAGE_THROUGH_FIRST_CHANNEL', + function (test) { + var message = "Message through 1st channel"; + var options = { + sourceChannel: test.pcLocal.dataChannels[0], + targetChannel: test.pcRemote.dataChannels[0] + }; - return test.send(message, options).then(result => { - is(test.pcRemote.dataChannels.indexOf(result.channel), 0, "1st channel used"); - is(result.data, message, "Received message has the correct content."); - }); - }, - + test.send(message, function (channel, data) { + is(test.pcRemote.dataChannels.indexOf(channel), 0, "1st channel used"); + is(data, message, "Received message has the correct content."); - function SEND_MESSAGE_BACK_THROUGH_FIRST_CHANNEL(test) { - var message = "Return a message also through 1st channel"; - var options = { - sourceChannel: test.pcRemote.dataChannels[0], - targetChannel: test.pcLocal.dataChannels[0] - }; + test.next(); + }, options); + } + ], + [ + 'SEND_MESSAGE_BACK_THROUGH_FIRST_CHANNEL', + function (test) { + var message = "Return a message also through 1st channel"; + var options = { + sourceChannel: test.pcRemote.dataChannels[0], + targetChannel: test.pcLocal.dataChannels[0] + }; - return test.send(message, options).then(result => { - is(test.pcLocal.dataChannels.indexOf(result.channel), 0, "1st channel used"); - is(result.data, message, "Return message has the correct content."); - }); - }, + test.send(message, function (channel, data) { + is(test.pcLocal.dataChannels.indexOf(channel), 0, "1st channel used"); + is(data, message, "Return message has the correct content."); - function CREATE_NEGOTIATED_DATA_CHANNEL(test) { - var options = { - negotiated:true, - id: 5, - protocol: "foo/bar", - ordered: false, - maxRetransmits: 500 - }; - return test.createDataChannel(options).then(result => { - var sourceChannel2 = result.local; - var targetChannel2 = result.remote; - is(sourceChannel2.readyState, "open", sourceChannel2 + " is in state: 'open'"); - is(targetChannel2.readyState, "open", targetChannel2 + " is in state: 'open'"); + test.next(); + }, options); + } + ], + [ + 'CREATE_NEGOTIATED_DATA_CHANNEL', + function (test) { + var options = {negotiated:true, id: 5, protocol:"foo/bar", ordered:false, + maxRetransmits:500}; + test.createDataChannel(options, function (sourceChannel2, targetChannel2) { + is(sourceChannel2.readyState, "open", sourceChannel2 + " is in state: 'open'"); + is(targetChannel2.readyState, "open", targetChannel2 + " is in state: 'open'"); - is(targetChannel2.binaryType, "blob", targetChannel2 + " is of binary type 'blob'"); + is(targetChannel2.binaryType, "blob", targetChannel2 + " is of binary type 'blob'"); + is(targetChannel2.readyState, "open", targetChannel2 + " is in state: 'open'"); - is(sourceChannel2.id, options.id, sourceChannel2 + " id is:" + sourceChannel2.id); - var reliable = !options.ordered ? false : (options.maxRetransmits || options.maxRetransmitTime); - is(sourceChannel2.protocol, options.protocol, sourceChannel2 + " protocol is:" + sourceChannel2.protocol); - is(sourceChannel2.reliable, reliable, sourceChannel2 + " reliable is:" + sourceChannel2.reliable); - /* - These aren't exposed by IDL yet + if (options.id != undefined) { + is(sourceChannel2.id, options.id, sourceChannel2 + " id is:" + sourceChannel2.id); + } + else { + options.id = sourceChannel2.id; + } + var reliable = !options.ordered ? false : (options.maxRetransmits || options.maxRetransmitTime); + is(sourceChannel2.protocol, options.protocol, sourceChannel2 + " protocol is:" + sourceChannel2.protocol); + is(sourceChannel2.reliable, reliable, sourceChannel2 + " reliable is:" + sourceChannel2.reliable); + /* + These aren't exposed by IDL yet is(sourceChannel2.ordered, options.ordered, sourceChannel2 + " ordered is:" + sourceChannel2.ordered); is(sourceChannel2.maxRetransmits, options.maxRetransmits, sourceChannel2 + " maxRetransmits is:" + - sourceChannel2.maxRetransmits); + sourceChannel2.maxRetransmits); is(sourceChannel2.maxRetransmitTime, options.maxRetransmitTime, sourceChannel2 + " maxRetransmitTime is:" + - sourceChannel2.maxRetransmitTime); - */ + sourceChannel2.maxRetransmitTime); + */ - is(targetChannel2.id, options.id, targetChannel2 + " id is:" + targetChannel2.id); - is(targetChannel2.protocol, options.protocol, targetChannel2 + " protocol is:" + targetChannel2.protocol); - is(targetChannel2.reliable, reliable, targetChannel2 + " reliable is:" + targetChannel2.reliable); - /* - These aren't exposed by IDL yet - is(targetChannel2.ordered, options.ordered, targetChannel2 + " ordered is:" + targetChannel2.ordered); + is(targetChannel2.id, options.id, targetChannel2 + " id is:" + targetChannel2.id); + is(targetChannel2.protocol, options.protocol, targetChannel2 + " protocol is:" + targetChannel2.protocol); + is(targetChannel2.reliable, reliable, targetChannel2 + " reliable is:" + targetChannel2.reliable); + /* + These aren't exposed by IDL yet + is(targetChannel2.ordered, options.ordered, targetChannel2 + " ordered is:" + targetChannel2.ordered); is(targetChannel2.maxRetransmits, options.maxRetransmits, targetChannel2 + " maxRetransmits is:" + - targetChannel2.maxRetransmits); + targetChannel2.maxRetransmits); is(targetChannel2.maxRetransmitTime, options.maxRetransmitTime, targetChannel2 + " maxRetransmitTime is:" + - targetChannel2.maxRetransmitTime); - */ - }); - }, + targetChannel2.maxRetransmitTime); + */ - function SEND_MESSAGE_THROUGH_LAST_OPENED_CHANNEL2(test) { - var channels = test.pcRemote.dataChannels; - var message = "I am the walrus; Goo goo g'joob"; + test.next(); + }); + } + ], + [ + 'SEND_MESSAGE_THROUGH_LAST_OPENED_CHANNEL2', + function (test) { + var channels = test.pcRemote.dataChannels; + var message = "Lorem ipsum dolor sit amet"; - return test.send(message).then(result => { - is(channels.indexOf(result.channel), channels.length - 1, "Last channel used"); - is(result.data, message, "Received message has the correct content."); - }); - } + test.send(message, function (channel, data) { + is(channels.indexOf(channel), channels.length - 1, "Last channel used"); + is(data, message, "Received message has the correct content."); + + test.next(); + }); + } + ] ]); }
--- a/dom/media/tests/mochitest/head.js +++ b/dom/media/tests/mochitest/head.js @@ -1,16 +1,15 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -"use strict"; - var Cc = SpecialPowers.Cc; var Ci = SpecialPowers.Ci; +var Cr = SpecialPowers.Cr; // Specifies whether we are using fake streams to run this automation var FAKE_ENABLED = true; try { var audioDevice = SpecialPowers.getCharPref('media.audio_loopback_dev'); var videoDevice = SpecialPowers.getCharPref('media.video_loopback_dev'); dump('TEST DEVICES: Using media devices:\n'); dump('audio: ' + audioDevice + '\nvideo: ' + videoDevice + '\n'); @@ -28,37 +27,37 @@ try { * Meta information of the test * @param {string} meta.title * Description of the test * @param {string} [meta.bug] * Bug the test was created for * @param {boolean} [meta.visible=false] * Visibility of the media elements */ -function realCreateHTML(meta) { +function createHTML(meta) { var test = document.getElementById('test'); // Create the head content var elem = document.createElement('meta'); elem.setAttribute('charset', 'utf-8'); document.head.appendChild(elem); var title = document.createElement('title'); title.textContent = meta.title; document.head.appendChild(title); // Create the body content var anchor = document.createElement('a'); - anchor.textContent = meta.title; + anchor.setAttribute('target', '_blank'); + if (meta.bug) { anchor.setAttribute('href', 'https://bugzilla.mozilla.org/show_bug.cgi?id=' + meta.bug); - } else { - anchor.setAttribute('target', '_blank'); } + anchor.textContent = meta.title; document.body.insertBefore(anchor, test); var display = document.createElement('p'); display.setAttribute('id', 'display'); document.body.insertBefore(display, test); var content = document.createElement('div'); content.setAttribute('id', 'content'); @@ -77,119 +76,110 @@ function realCreateHTML(meta) { * Description to use for the element * @return {HTMLMediaElement} The created HTML media element */ function createMediaElement(type, label) { var id = label + '_' + type; var element = document.getElementById(id); // Sanity check that we haven't created the element already - if (element) { + if (element) return element; - } element = document.createElement(type === 'audio' ? 'audio' : 'video'); element.setAttribute('id', id); element.setAttribute('height', 100); element.setAttribute('width', 150); element.setAttribute('controls', 'controls'); - element.setAttribute('autoplay', 'autoplay'); document.getElementById('content').appendChild(element); return element; } /** * Wrapper function for mozGetUserMedia to allow a singular area of control * for determining whether we run this with fake devices or not. * * @param {Dictionary} constraints * The constraints for this mozGetUserMedia callback + * @param {Function} onSuccess + * The success callback if the stream is successfully retrieved + * @param {Function} onError + * The error callback if the stream fails to be retrieved */ -function getUserMedia(constraints) { +function getUserMedia(constraints, onSuccess, onError) { if (!("fake" in constraints) && FAKE_ENABLED) { constraints["fake"] = FAKE_ENABLED; } info("Call getUserMedia for " + JSON.stringify(constraints)); - return navigator.mediaDevices.getUserMedia(constraints); + navigator.mozGetUserMedia(constraints, onSuccess, onError); } -// These are the promises we use to track that the prerequisites for the test -// are in place before running it. Users of this file need to ensure that they -// also provide a promise called `scriptsReady` as well. -var setTestOptions; -var testConfigured = new Promise(r => setTestOptions = r); -function setupEnvironment() { - if (!window.SimpleTest) { - return Promise.resolve(); - } - - // Running as a Mochitest. - SimpleTest.requestFlakyTimeout("WebRTC inherently depends on timeouts"); - window.finish = () => SimpleTest.finish(); - SpecialPowers.pushPrefEnv({ - 'set': [ +/** + * Setup any Mochitest for WebRTC by enabling the preference for + * peer connections. As by bug 797979 it will also enable mozGetUserMedia() + * and disable the mozGetUserMedia() permission checking. + * + * @param {Function} aCallback + * Test method to execute after initialization + */ +function runTest(aCallback) { + if (window.SimpleTest) { + // Running as a Mochitest. + SimpleTest.waitForExplicitFinish(); + SimpleTest.requestFlakyTimeout("WebRTC inherently depends on timeouts"); + SpecialPowers.pushPrefEnv({'set': [ ['dom.messageChannel.enabled', true], ['media.peerconnection.enabled', true], ['media.peerconnection.identity.enabled', true], ['media.peerconnection.identity.timeout', 12000], ['media.peerconnection.default_iceservers', '[]'], ['media.navigator.permission.disabled', true], ['media.getusermedia.screensharing.enabled', true], - ['media.getusermedia.screensharing.allowed_domains', "mochi.test"] - ] - }, setTestOptions); + ['media.getusermedia.screensharing.allowed_domains', "mochi.test"]] + }, function () { + try { + aCallback(); + } + catch (err) { + generateErrorCallback()(err); + } + }); + } else { + // Steeplechase, let it call the callback. + window.run_test = function(is_initiator) { + var options = {is_local: is_initiator, + is_remote: !is_initiator}; + aCallback(options); + }; + // Also load the steeplechase test code. + var s = document.createElement("script"); + s.src = "/test.js"; + document.head.appendChild(s); + } } -// This is called by steeplechase; which provides the test configuration options -// directly to the test through this function. If we're not on steeplechase, -// the test is configured directly and immediately. -function run_test(is_initiator) { - var options = { is_local: is_initiator, - is_remote: !is_initiator }; - - // Also load the steeplechase test code. - var s = document.createElement("script"); - s.src = "/test.js"; - s.onload = () => setTestOptions(options); - document.head.appendChild(s); -} - -function runTestWhenReady(testFunc) { - setupEnvironment(); - return Promise.all([scriptsReady, testConfigured]).then(() => { - try { - return testConfigured.then(options => testFunc(options)); - } catch (e) { - ok(false, 'Error executing test: ' + e + - ((typeof e.stack === 'string') ? - (' ' + e.stack.split('\n').join(' ... ')) : '')); - } - }); -} - - /** * Checks that the media stream tracks have the expected amount of tracks * with the correct kind and id based on the type and constraints given. * * @param {Object} constraints specifies whether the stream should have * audio, video, or both * @param {String} type the type of media stream tracks being checked * @param {sequence<MediaStreamTrack>} mediaStreamTracks the media stream * tracks being checked */ function checkMediaStreamTracksByType(constraints, type, mediaStreamTracks) { - if (constraints[type]) { + if(constraints[type]) { is(mediaStreamTracks.length, 1, 'One ' + type + ' track shall be present'); - if (mediaStreamTracks.length) { + if(mediaStreamTracks.length) { is(mediaStreamTracks[0].kind, type, 'Track kind should be ' + type); ok(mediaStreamTracks[0].id, 'Track id should be defined'); } } else { is(mediaStreamTracks.length, 0, 'No ' + type + ' tracks shall be present'); } } @@ -203,37 +193,39 @@ function checkMediaStreamTracksByType(co */ function checkMediaStreamTracks(constraints, mediaStream) { checkMediaStreamTracksByType(constraints, 'audio', mediaStream.getAudioTracks()); checkMediaStreamTracksByType(constraints, 'video', mediaStream.getVideoTracks()); } -/*** Utility methods */ - -/** The dreadful setTimeout, use sparingly */ -function wait(time) { - return new Promise(r => setTimeout(r, time)); -} +/** + * Utility methods + */ -/** The even more dreadful setInterval, use even more sparingly */ -function waitUntil(func, time) { - return new Promise(resolve => { - var interval = setInterval(() => { - if (func()) { - clearInterval(interval); - resolve(); - } - }, time || 200); - }); +/** + * Returns the contents of a blob as text + * + * @param {Blob} blob + The blob to retrieve the contents from + * @param {Function} onSuccess + Callback with the blobs content as parameter + */ +function getBlobContent(blob, onSuccess) { + var reader = new FileReader(); + + // Listen for 'onloadend' which will always be called after a success or failure + reader.onloadend = function (event) { + onSuccess(event.target.result); + }; + + reader.readAsText(blob); } -/*** Test control flow methods */ - /** * Generates a callback function fired only under unexpected circumstances * while running the tests. The generated function kills off the test as well * gracefully. * * @param {String} [message] * An optional message to show if no object gets passed into the * generated callback method. @@ -241,247 +233,60 @@ function waitUntil(func, time) { function generateErrorCallback(message) { var stack = new Error().stack.split("\n"); stack.shift(); // Don't include this instantiation frame /** * @param {object} aObj * The object fired back from the callback */ - return aObj => { + return function (aObj) { if (aObj) { if (aObj.name && aObj.message) { ok(false, "Unexpected callback for '" + aObj.name + "' with message = '" + aObj.message + "' at " + JSON.stringify(stack)); } else { ok(false, "Unexpected callback with = '" + aObj + "' at: " + JSON.stringify(stack)); } } else { ok(false, "Unexpected callback with message = '" + message + "' at: " + JSON.stringify(stack)); } - throw new Error("Unexpected callback"); + SimpleTest.finish(); } } -var unexpectedEventArrived; -var rejectOnUnexpectedEvent = new Promise((x, reject) => { - unexpectedEventArrived = reject; -}); - /** * Generates a callback function fired only for unexpected events happening. * * @param {String} description Description of the object for which the event has been fired * @param {String} eventName Name of the unexpected event */ -function unexpectedEvent(message, eventName) { +function unexpectedEventAndFinish(message, eventName) { var stack = new Error().stack.split("\n"); stack.shift(); // Don't include this instantiation frame - return e => { - var details = "Unexpected event '" + eventName + "' fired with message = '" + - message + "' at: " + JSON.stringify(stack); - ok(false, details); - unexpectedEventArrived(new Error(details)); + return function () { + ok(false, "Unexpected event '" + eventName + "' fired with message = '" + + message + "' at: " + JSON.stringify(stack)); + SimpleTest.finish(); } } -/** - * Implements the one-shot event pattern used throughout. Each of the 'onxxx' - * attributes on the wrappers can be set with a custom handler. Prior to the - * handler being set, if the event fires, it causes the test execution to halt. - * That handler is used exactly once, after which the original, error-generating - * handler is re-installed. Thus, each event handler is used at most once. - * - * @param {object} wrapper - * The wrapper on which the psuedo-handler is installed - * @param {object} obj - * The real source of events - * @param {string} event - * The name of the event - */ -function createOneShotEventWrapper(wrapper, obj, event) { - var onx = 'on' + event; - var unexpected = unexpectedEvent(wrapper, event); - wrapper[onx] = unexpected; - obj[onx] = e => { - info(wrapper + ': "on' + event + '" event fired'); - e.wrapper = wrapper; - wrapper[onx](e); - wrapper[onx] = unexpected; - }; -} +function IsMacOSX10_6orOlder() { + var is106orOlder = false; - -/** - * This class executes a series of functions in a continuous sequence. - * Promise-bearing functions are executed after the previous promise completes. - * - * @constructor - * @param {object} framework - * A back reference to the framework which makes use of the class. It is - * passed to each command callback. - * @param {function[]} commandList - * Commands to set during initialization - */ -function CommandChain(framework, commandList) { - this._framework = framework; - this.commands = commandList || [ ]; + if (navigator.platform.indexOf("Mac") == 0) { + var version = Cc["@mozilla.org/system-info;1"] + .getService(SpecialPowers.Ci.nsIPropertyBag2) + .getProperty("version"); + // the next line is correct: Mac OS 10.6 corresponds to Darwin version 10.x ! + // Mac OS 10.7 is Darwin version 11.x. the |version| string we've got here + // is the Darwin version. + is106orOlder = (parseFloat(version) < 11.0); + } + return is106orOlder; } -CommandChain.prototype = { - /** - * Start the command chain. This returns a promise that always resolves - * cleanly (this catches errors and fails the test case). - */ - execute: function () { - return this.commands.reduce((prev, next, i) => { - if (typeof next !== 'function' || !next.name) { - throw new Error('registered non-function' + next); - } - - return prev.then(() => { - info('Run step ' + (i + 1) + ': ' + next.name); - return Promise.race([ next(this._framework), rejectOnUnexpectedEvent ]); - }); - }, Promise.resolve()) - .catch(e => - ok(false, 'Error in test execution: ' + e + - ((typeof e.stack === 'string') ? - (' ' + e.stack.split('\n').join(' ... ')) : ''))); - }, - - /** - * Add new commands to the end of the chain - */ - append: function(commands) { - this.commands = this.commands.concat(commands); - }, - - /** - * Returns the index of the specified command in the chain. - */ - indexOf: function(functionOrName) { - if (typeof functionOrName === 'string') { - return this.commands.findIndex(f => f.name === functionOrName); - } - return this.commands.indexOf(functionOrName); - }, - - /** - * Inserts the new commands after the specified command. - */ - insertAfter: function(functionOrName, commands) { - this._insertHelper(functionOrName, commands, 1); - }, - - /** - * Inserts the new commands before the specified command. - */ - insertBefore: function(functionOrName, commands) { - this._insertHelper(functionOrName, commands, 0); - }, - - _insertHelper: function(functionOrName, commands, delta) { - var index = this.indexOf(functionOrName); - - if (index >= 0) { - this.commands = [].concat( - this.commands.slice(0, index + delta), - commands, - this.commands.slice(index + delta)); - } - }, - - /** - * Removes the specified command, returns what was removed. - */ - remove: function(functionOrName) { - var index = this.indexOf(functionOrName); - if (index >= 0) { - return this.commands.splice(index, 1); - } - return []; - }, - - /** - * Removes all commands after the specified one, returns what was removed. - */ - removeAfter: function(functionOrName) { - var index = this.indexOf(functionOrName); - if (index >= 0) { - return this.commands.splice(index + 1); - } - return []; - }, - - /** - * Removes all commands before the specified one, returns what was removed. - */ - removeBefore: function(functionOrName) { - var index = this.indexOf(functionOrName); - if (index >= 0) { - return this.commands.splice(0, index); - } - return []; - }, - - /** - * Replaces a single command, returns what was removed. - */ - replace: function(functionOrName, commands) { - this.insertBefore(functionOrName, commands); - return this.remove(functionOrName); - }, - - /** - * Replaces all commands after the specified one, returns what was removed. - */ - replaceAfter: function(functionOrName, commands) { - var oldCommands = this.removeAfter(functionOrName); - this.append(commands); - return oldCommands; - }, - - /** - * Replaces all commands before the specified one, returns what was removed. - */ - replaceBefore: function(functionOrName, commands) { - var oldCommands = this.removeBefore(functionOrName); - this.insertBefore(functionOrName, commands); - return oldCommands; - }, - - /** - * Remove all commands whose name match the specified regex. - */ - filterOut: function (id_match) { - this.commands = this.commands.filter(c => !id_match.test(c.name)); - } -}; - - -function IsMacOSX10_6orOlder() { - if (navigator.platform.indexOf("Mac") !== 0) { - return false; - } - - var version = Cc["@mozilla.org/system-info;1"] - .getService(Ci.nsIPropertyBag2) - .getProperty("version"); - // the next line is correct: Mac OS 10.6 corresponds to Darwin version 10.x ! - // Mac OS 10.7 is Darwin version 11.x. the |version| string we've got here - // is the Darwin version. - return (parseFloat(version) < 11.0); -} - -(function(){ - var el = document.createElement("link"); - el.rel = "stylesheet"; - el.type = "text/css"; - el.href= "/tests/SimpleTest/test.css"; - document.head.appendChild(el); -}());
--- a/dom/media/tests/mochitest/identity/identityevent.js +++ b/dom/media/tests/mochitest/identity/identityevent.js @@ -1,12 +1,12 @@ (function(g) { 'use strict'; - g.trapIdentityEvents = target => { + g.trapIdentityEvents = function(target) { var state = {}; var identityEvents = ['idpassertionerror', 'idpvalidationerror', 'identityresult', 'peeridentity']; identityEvents.forEach(function(name) { target.addEventListener(name, function(e) { state[name] = e; }, false); });
--- a/dom/media/tests/mochitest/identity/test_getIdentityAssertion.html +++ b/dom/media/tests/mochitest/identity/test_getIdentityAssertion.html @@ -1,110 +1,117 @@ <!DOCTYPE HTML> <html> <head> - <script type="application/javascript">var scriptRelativePath = "../";</script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="../head.js"></script> <script type="application/javascript" src="../pc.js"></script> + <script type="application/javascript" src="../templates.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ - title: "getIdentityAssertion Tests", - bug: "942367" + title: "getIdentityAssertion Tests" }); function checkIdentity(assertion, identity) { // here we dig into the payload, which means we need to know something // about how the IdP actually works (not good in general, but OK here) var assertion = JSON.parse(atob(assertion)).assertion; var user = JSON.parse(assertion).username; is(user, identity, "id should be '" + identity + "' is '" + user + "'"); } var test; function theTest() { test = new PeerConnectionTest(); test.setMediaConstraints([{audio: true}], [{audio: true}]); test.chain.removeAfter('PC_REMOTE_CHECK_INITIAL_SIGNALINGSTATE'); test.chain.append([ - function GET_IDENTITY_ASSERTION_FAILS_WITHOUT_PROVIDER(test) { - return new Promise(resolve => { - test.pcLocal._pc.onidpassertionerror = function(e) { - ok(e, "getIdentityAssertion must fail without provider"); - resolve(); - }; - test.pcLocal._pc.getIdentityAssertion(); - }); - }, - function GET_IDENTITY_ASSERTION_FIRES_EVENTUALLY_AND_SUBSEQUENTLY(test) { - return new Promise(resolve => { - var fired = 0; - test.setIdentityProvider(test.pcLocal, 'example.com', 'idp.html'); - test.pcLocal._pc.onidentityresult = function(e) { - fired++; - if (fired == 1) { - ok(true, "identityresult fired"); - checkIdentity(e.assertion, 'someone@example.com'); - } else if (fired == 2) { - ok(true, "identityresult fired 2x"); - checkIdentity(e.assertion, 'someone@example.com'); - resolve(); - } - }; - test.pcLocal._pc.onidpassertionerror = function(e) { - ok(false, "error event fired"); - resolve(); - }; - test.pcLocal._pc.getIdentityAssertion(); - test.pcLocal._pc.getIdentityAssertion(); - }); + [ + "GET_IDENTITY_ASSERTION_FAILS_WITHOUT_PROVIDER", + function(test) { + test.pcLocal._pc.onidpassertionerror = function(e) { + ok(e, "getIdentityAssertion must fail without provider"); + test.next(); + }; + test.pcLocal._pc.getIdentityAssertion(); }, - function GET_IDENTITY_ASSERTION_FAILS(test) { - return new Promise(resolve => { - test.setIdentityProvider(test.pcLocal, 'example.com', 'idp.html#error'); - test.pcLocal._pc.onidentityresult = function(e) { - ok(false, "Should not get an identity result"); - resolve(); - }; - test.pcLocal._pc.onidpassertionerror = function(err) { - ok(err, "Got error event from getIdentityAssertion"); - resolve(); - }; - test.pcLocal._pc.getIdentityAssertion(); - }); - }, - function GET_IDENTITY_ASSERTION_IDP_NOT_READY(test) { - return new Promise(resolve => { - test.setIdentityProvider(test.pcLocal, 'example.com', 'idp.html#error:ready'); - test.pcLocal._pc.onidentityresult = function(e) { - ok(false, "Should not get an identity result"); - resolve(); - }; - test.pcLocal._pc.onidpassertionerror = function(e) { - ok(e, "Got error callback from getIdentityAssertion"); - resolve(); - }; - test.pcLocal._pc.getIdentityAssertion(); - }); - }, - function GET_IDENTITY_ASSERTION_WITH_SPECIFIC_NAME(test) { - return new Promise(resolve => { - test.setIdentityProvider(test.pcLocal, 'example.com', 'idp.html', 'user@example.com'); - test.pcLocal._pc.onidentityresult = function(e) { - checkIdentity(e.assertion, 'user@example.com'); - resolve(); - }; - test.pcLocal._pc.onidpassertionerror = function(e) { - ok(false, "Got error callback from getIdentityAssertion"); - resolve(); - }; - test.pcLocal._pc.getIdentityAssertion(); - }); + ], + [ + "GET_IDENTITY_ASSERTION_FIRES_EVENTUALLY_AND_SUBSEQUENTLY", + function(test) { + var fired = 0; + test.setIdentityProvider(test.pcLocal, 'example.com', 'idp.html'); + test.pcLocal._pc.onidentityresult = function(e) { + fired++; + if (fired == 1) { + ok(true, "identityresult fired"); + checkIdentity(e.assertion, 'someone@example.com'); + } else if (fired == 2) { + ok(true, "identityresult fired 2x"); + checkIdentity(e.assertion, 'someone@example.com'); + test.next(); + } + }; + test.pcLocal._pc.onidpassertionerror = function(e) { + ok(false, "error event fired"); + test.next(); + }; + test.pcLocal._pc.getIdentityAssertion(); + test.pcLocal._pc.getIdentityAssertion(); } + ], + [ + "GET_IDENTITY_ASSERTION_FAILS", + function(test) { + test.setIdentityProvider(test.pcLocal, 'example.com', 'idp.html#error'); + test.pcLocal._pc.onidentityresult = function(e) { + ok(false, "Should not get an identity result"); + test.next(); + }; + test.pcLocal._pc.onidpassertionerror = function(err) { + ok(err, "Got error event from getIdentityAssertion"); + test.next(); + }; + test.pcLocal._pc.getIdentityAssertion(); + } + ], + [ + "GET_IDENTITY_ASSERTION_IDP_NOT_READY", + function(test) { + test.setIdentityProvider(test.pcLocal, 'example.com', 'idp.html#error:ready'); + test.pcLocal._pc.onidentityresult = function(e) { + ok(false, "Should not get an identity result"); + test.next(); + }; + test.pcLocal._pc.onidpassertionerror = function(e) { + ok(e, "Got error callback from getIdentityAssertion"); + test.next(); + }; + test.pcLocal._pc.getIdentityAssertion(); + } + ], + [ + "GET_IDENTITY_ASSERTION_WITH_SPECIFIC_NAME", + function(test) { + test.setIdentityProvider(test.pcLocal, 'example.com', 'idp.html', 'user@example.com'); + test.pcLocal._pc.onidentityresult = function(e) { + checkIdentity(e.assertion, 'user@example.com'); + test.next(); + }; + test.pcLocal._pc.onidpassertionerror = function(e) { + ok(false, "Got error callback from getIdentityAssertion"); + test.next(); + }; + test.pcLocal._pc.getIdentityAssertion(); + } + ] ]); test.run(); } runNetworkTest(theTest); </script> </pre> </body>
--- a/dom/media/tests/mochitest/identity/test_peerConnection_peerIdentity.html +++ b/dom/media/tests/mochitest/identity/test_peerConnection_peerIdentity.html @@ -1,21 +1,26 @@ <!DOCTYPE HTML> <html> <head> - <script type="application/javascript">var scriptRelativePath = "../";</script> + <meta charset="utf-8"/> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="../head.js"></script> <script type="application/javascript" src="../pc.js"></script> + <script type="application/javascript" src="../templates.js"></script> <script type="application/javascript" src="../blacksilence.js"></script> + <script type="application/javascript" src="../turnConfig.js"></script> </head> <body> +<div id="display"></div> <pre id="test"> <script type="application/javascript"> createHTML({ - title: "setIdentityProvider leads to peerIdentity and assertions in SDP", - bug: "942367" + title: "setIdentityProvider leads to peerIdentity and assertions in SDP" }); var test; function theTest() { var id1 = 'someone@test1.example.com'; var id2 = 'someone@test2.example.com'; test = new PeerConnectionTest({ config_local: { @@ -38,36 +43,48 @@ function theTest() { }, { video: true, fake: true, peerIdentity: id1 }]); test.setIdentityProvider(test.pcLocal, 'test1.example.com', 'idp.html'); test.setIdentityProvider(test.pcRemote, 'test2.example.com', 'idp.html'); test.chain.append([ - - function PEER_IDENTITY_IS_SET_CORRECTLY(test) { + [ + "PEER_IDENTITY_IS_SET_CORRECTLY", + function(test) { // no need to wait to check identity in this case, // setRemoteDescription should wait for the IdP to complete function checkIdentity(pc, pfx, idp, name) { is(pc.peerIdentity.idp, idp, pfx + "IdP is correct"); is(pc.peerIdentity.name, name + "@" + idp, pfx + "identity is correct"); } checkIdentity(test.pcLocal._pc, "local: ", "test2.example.com", "someone"); checkIdentity(test.pcRemote._pc, "remote: ", "test1.example.com", "someone"); - }, - - function REMOTE_STREAMS_ARE_RESTRICTED(test) { + test.next(); + } + ], + [ + "REMOTE_STREAMS_ARE_RESTRICTED", + function(test) { var remoteStream = test.pcLocal._pc.getRemoteStreams()[0]; - return Promise.all([ - new Promise(done => audioIsSilence(true, remoteStream, done)), - new Promise(done => videoIsBlack(true, remoteStream, done)) - ]); + var oneDone = false; + function done() { + if (!oneDone) { + oneDone = true; + return; + } + test.next(); + } + + audioIsSilence(true, remoteStream, done); + videoIsBlack(true, remoteStream, done); } + ], ]); test.run(); } runNetworkTest(theTest); </script> </pre> </body>
--- a/dom/media/tests/mochitest/identity/test_setIdentityProvider.html +++ b/dom/media/tests/mochitest/identity/test_setIdentityProvider.html @@ -1,95 +1,115 @@ <!DOCTYPE HTML> <html> <head> - <script type="application/javascript">var scriptRelativePath = "../";</script> + <meta charset="utf-8"/> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="../head.js"></script> <script type="application/javascript" src="../pc.js"></script> + <script type="application/javascript" src="../templates.js"></script> <script type="application/javascript" src="identityevent.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ - title: "setIdentityProvider leads to peerIdentity and assertions in SDP", - bug: "942367" + title: "setIdentityProvider leads to peerIdentity and assertions in SDP" }); var test; function theTest() { test = new PeerConnectionTest(); test.setMediaConstraints([{audio: true}], [{audio: true}]); test.setIdentityProvider(test.pcLocal, "test1.example.com", "idp.html", "someone"); test.setIdentityProvider(test.pcRemote, "test2.example.com", "idp.html", "someone"); var localEvents = trapIdentityEvents(test.pcLocal._pc); var remoteEvents = trapIdentityEvents(test.pcRemote._pc); test.chain.append([ - function PEER_IDENTITY_IS_SET_CORRECTLY(test) { + [ + "PEER_IDENTITY_IS_SET_CORRECTLY", + function(test) { var outstanding = 0; // we have to wait for the identity result in order to get the actual // identity information, since the call will complete before the identity // provider has a chance to finish verifying... that's OK, but it makes // testing more difficult - function checkOrSetupCheck(pc, prefix, idp, name) { + function checkOrSetupCheck(pc, pfx, idp, name) { function checkIdentity() { - ok(pc.peerIdentity, prefix + "peerIdentity is set"); - is(pc.peerIdentity.idp, idp, prefix + "IdP is correct"); - is(pc.peerIdentity.name, name + "@" + idp, prefix + "identity is correct"); + ok(pc.peerIdentity, pfx + "peerIdentity is set"); + is(pc.peerIdentity.idp, idp, pfx + "IdP is correct"); + is(pc.peerIdentity.name, name + "@" + idp, pfx + "identity is correct"); } if (pc.peerIdentity) { - info(prefix + "peerIdentity already set"); + info(pfx + "peerIdentity already set"); checkIdentity(); - return Promise.resolve(); + } else { + ++outstanding; + info(pfx + "setting onpeeridentity handler"); + pc.onpeeridentity = function checkIdentityEvent(e) { + info(pfx + "checking peerIdentity"); + checkIdentity(); + --outstanding; + if (outstanding <= 0) { + test.next(); + } + }; } - - return new Promise(resolve => { - info(prefix + "setting onpeeridentity handler"); - pc.onpeeridentity = e => { - checkIdentity(); - resolve(); - }; - }); } - return Promise.all([ - checkOrSetupCheck(test.pcLocal._pc, "local: ", "test2.example.com", "someone"), - checkOrSetupCheck(test.pcRemote._pc, "remote: ", "test1.example.com", "someone") - ]); - }, - - function CHECK_IDENTITY_EVENTS(test) { + checkOrSetupCheck(test.pcLocal._pc, "local: ", "test2.example.com", "someone"); + checkOrSetupCheck(test.pcRemote._pc, "remote: ", "test1.example.com", "someone"); + if (outstanding <= 0) { + test.next(); + } + } + ], + [ + "CHECK_IDENTITY_EVENTS", + function(test) { ok(!localEvents.idpassertionerror , "No assertion generation errors on local"); ok(!remoteEvents.idpassertionerror, "No assertion generation errors on remote"); ok(!localEvents.idpvalidationerror, "No assertion validation errors on local"); - ok(!remoteEvents.idpvalidationerror, "No assertion validation errors on remote"); + ok( !remoteEvents.idpvalidationerror, "No assertion validation errors on remote"); ok(localEvents.identityresult, "local acquired identity assertions"); ok(remoteEvents.identityresult, "remote acquired identity assertions"); ok(localEvents.peeridentity, "local got peer identity"); ok(remoteEvents.peeridentity, "remote got peer identity"); - }, - - function OFFERS_AND_ANSWERS_INCLUDE_IDENTITY(test) { + test.next(); + } + ], + [ + "OFFERS_AND_ANSWERS_INCLUDE_IDENTITY", + function(test) { ok(test.originalOffer.sdp.contains("a=identity"), "a=identity is in the offer SDP"); ok(test.originalAnswer.sdp.contains("a=identity"), "a=identity is in the answer SDP"); - }, - - function DESCRIPTIONS_CONTAIN_IDENTITY(test) { + test.next(); + } + ], + [ + "DESCRIPTIONS_CONTAIN_IDENTITY", + function(test) { ok(test.pcLocal.localDescription.sdp.contains("a=identity"), - "a=identity is in the local copy of the offer"); + "a=identity is in the local copy of the offer"); ok(test.pcRemote.localDescription.sdp.contains("a=identity"), - "a=identity is in the remote copy of the offer"); + "a=identity is in the remote copy of the offer"); ok(test.pcLocal.remoteDescription.sdp.contains("a=identity"), - "a=identity is in the local copy of the answer"); + "a=identity is in the local copy of the answer"); ok(test.pcRemote.remoteDescription.sdp.contains("a=identity"), - "a=identity is in the remote copy of the answer"); + "a=identity is in the remote copy of the answer"); + test.next(); } + ] ]); test.run(); } runNetworkTest(theTest); + + </script> </pre> </body> </html>
--- a/dom/media/tests/mochitest/identity/test_setIdentityProviderWithErrors.html +++ b/dom/media/tests/mochitest/identity/test_setIdentityProviderWithErrors.html @@ -1,74 +1,85 @@ <!DOCTYPE HTML> <html> <head> - <script type="application/javascript">var scriptRelativePath = "../";</script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="../head.js"></script> <script type="application/javascript" src="../pc.js"></script> + <script type="application/javascript" src="../templates.js"></script> <script type="application/javascript" src="identityevent.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ - title: "Identity Provider returning errors is handled correctly", - bug: "942367" + title: "Identity Provider returning errors is handled correctly" }); +var test; runNetworkTest(function () { - var test = new PeerConnectionTest(); + test = new PeerConnectionTest(); test.setMediaConstraints([{audio: true}], [{audio: true}]); // first example generates an error test.setIdentityProvider(test.pcLocal, 'example.com', 'idp.html#error', 'nobody'); // second generates a bad assertion; which fails to validate test.setIdentityProvider(test.pcRemote, 'example.com', 'idp.html#bad', 'nobody'); var localEvents = trapIdentityEvents(test.pcLocal._pc); var remoteEvents = trapIdentityEvents(test.pcRemote._pc); test.chain.append([ - function CHECK_IDENTITY_EVENTS(test) { - function checkEvents() { - ok(localEvents.idpassertionerror, 'local assertion generation should fail (idpassertionerror)'); - is(localEvents.idpassertionerror.idp, 'example.com', 'event IdP is correct'); - is(localEvents.idpassertionerror.protocol, 'idp.html#error', 'event IdP protocol is #error'); - ok(!remoteEvents.idpassertionerror, 'remote assertion generation should succeed (idpassertionerror)'); - ok(!localEvents.identityresult, 'local assertion generation should fail (identityresult)'); - ok(remoteEvents.identityresult, 'remote assertion generation should succeed (identityresult)'); + [ + 'CHECK_IDENTITY_EVENTS', + function(test) { + function checkEvents() { + ok(localEvents.idpassertionerror, 'local assertion generation should fail (idpassertionerror)'); + is(localEvents.idpassertionerror.idp, 'example.com', 'event IdP is correct'); + is(localEvents.idpassertionerror.protocol, 'idp.html#error', 'event IdP protocol is #error'); + ok(!remoteEvents.idpassertionerror, 'remote assertion generation should succeed (idpassertionerror)'); + ok(!localEvents.identityresult, 'local assertion generation should fail (identityresult)'); + ok(remoteEvents.identityresult, 'remote assertion generation should succeed (identityresult)'); - ok(!localEvents.peeridentity, 'no peer identity event for local peer'); - ok(!remoteEvents.peeridentity, 'no peer identity event for remote peer'); - ok(localEvents.idpvalidationerror, 'local fails to validate'); - is(localEvents.idpvalidationerror.idp, 'example.com', 'event IdP is correct'); - is(localEvents.idpvalidationerror.protocol, 'idp.html#bad', 'event IdP protocol is #bad'); - ok(!remoteEvents.idpvalidationerror, 'remote doesn\'t even see an assertion'); + ok(!localEvents.peeridentity, 'no peer identity event for local peer'); + ok(!remoteEvents.peeridentity, 'no peer identity event for remote peer'); + ok(localEvents.idpvalidationerror, 'local fails to validate'); + is(localEvents.idpvalidationerror.idp, 'example.com', 'event IdP is correct'); + is(localEvents.idpvalidationerror.protocol, 'idp.html#bad', 'event IdP protocol is #bad'); + ok(!remoteEvents.idpvalidationerror, 'remote doesn\'t even see an assertion'); - } + test.next(); + } - // we actually have to wait on this because IdP validation happens asynchronously - if (localEvents.idpvalidationerror) { - checkEvents(); - return Promise.resolve(); + // we actually have to wait on this because IdP validation happens asynchronously + if (localEvents.idpvalidationerror) { + checkEvents(); + } else { + // have to let the other event handler have a chance to record success + // before we run the checks that rely on that recording + test.pcLocal._pc.onidpvalidationerror = setTimeout.bind(window, checkEvents, 1); + } } - // have to let the other event handler have a chance to record success - // before we run the checks that rely on that recording - return new Promise(resolve => { - test.pcLocal._pc.onidpvalidationerror = resolve; - }).then(checkEvents); - }, - - function PEER_IDENTITY_IS_EMPTY(test) { - ok(!test.pcLocal._pc.peerIdentity, 'local peerIdentity is not set'); - ok(!test.pcRemote._pc.peerIdentity, 'remote peerIdentity is not set'); - }, - - function ONLY_REMOTE_SDP_INCLUDES_IDENTITY_ASSERTION(test) { - ok(!test.originalOffer.sdp.contains('a=identity'), 'a=identity not contained in the offer SDP'); - ok(test.originalAnswer.sdp.contains('a=identity'), 'a=identity is contained in the answer SDP'); - } + ], + [ + 'PEER_IDENTITY_IS_EMPTY', + function(test) { + ok(!test.pcLocal._pc.peerIdentity, 'local peerIdentity is not set'); + ok(!test.pcRemote._pc.peerIdentity, 'remote peerIdentity is not set'); + test.next(); + } + ], + [ + 'ONLY_REMOTE_SDP_INCLUDES_IDENTITY_ASSERTION', + function(test) { + ok(!test.originalOffer.sdp.contains('a=identity'), 'a=identity not contained in the offer SDP'); + ok(test.originalAnswer.sdp.contains('a=identity'), 'a=identity is contained in the answer SDP'); + test.next(); + } + ] ]); test.run(); }); </script> </pre> </body> </html>
--- a/dom/media/tests/mochitest/mediaStreamPlayback.js +++ b/dom/media/tests/mochitest/mediaStreamPlayback.js @@ -32,123 +32,137 @@ MediaStreamPlayback.prototype = { * * @param {Boolean} isResume specifies if this media element is being resumed * from a previous run * @param {Function} onSuccess the success callback if the media playback * start and stop cycle completes successfully * @param {Function} onError the error callback if the media playback * start and stop cycle fails */ - playMedia : function(isResume) { - return this.startMedia(isResume) - .then(() => this.stopMediaElement()); + playMedia : function MSP_playMedia(isResume, onSuccess, onError) { + var self = this; + + this.startMedia(isResume, function() { + self.stopMediaElement(); + onSuccess(); + }, onError); }, /** * Starts the media with the associated stream. * * @param {Boolean} isResume specifies if the media element playback * is being resumed from a previous run + * @param {Function} onSuccess the success function call back + * if media starts correctly + * @param {Function} onError the error function call back + * if media fails to start */ - startMedia : function(isResume) { + startMedia : function MSP_startMedia(isResume, onSuccess, onError) { + var self = this; var canPlayThroughFired = false; // If we're initially running this media, check that the time is zero if (!isResume) { is(this.mediaStream.currentTime, 0, "Before starting the media element, currentTime = 0"); } - return new Promise((resolve, reject) => { - /** - * Callback fired when the canplaythrough event is fired. We only - * run the logic of this function once, as this event can fire - * multiple times while a HTMLMediaStream is playing content from - * a real-time MediaStream. - */ - var canPlayThroughCallback = () => { - // Disable the canplaythrough event listener to prevent multiple calls - canPlayThroughFired = true; - this.mediaElement.removeEventListener('canplaythrough', - canPlayThroughCallback, false); + /** + * Callback fired when the canplaythrough event is fired. We only + * run the logic of this function once, as this event can fire + * multiple times while a HTMLMediaStream is playing content from + * a real-time MediaStream. + */ + var canPlayThroughCallback = function() { + // Disable the canplaythrough event listener to prevent multiple calls + canPlayThroughFired = true; + self.mediaElement.removeEventListener('canplaythrough', + canPlayThroughCallback, false); - is(this.mediaElement.paused, false, - "Media element should be playing"); - is(this.mediaElement.duration, Number.POSITIVE_INFINITY, - "Duration should be infinity"); + is(self.mediaElement.paused, false, + "Media element should be playing"); + is(self.mediaElement.duration, Number.POSITIVE_INFINITY, + "Duration should be infinity"); - // When the media element is playing with a real-time stream, we - // constantly switch between having data to play vs. queuing up data, - // so we can only check that the ready state is one of those two values - ok(this.mediaElement.readyState === HTMLMediaElement.HAVE_ENOUGH_DATA || - this.mediaElement.readyState === HTMLMediaElement.HAVE_CURRENT_DATA, - "Ready state shall be HAVE_ENOUGH_DATA or HAVE_CURRENT_DATA"); - - is(this.mediaElement.seekable.length, 0, - "Seekable length shall be zero"); - is(this.mediaElement.buffered.length, 0, - "Buffered length shall be zero"); + // When the media element is playing with a real-time stream, we + // constantly switch between having data to play vs. queuing up data, + // so we can only check that the ready state is one of those two values + ok(self.mediaElement.readyState === HTMLMediaElement.HAVE_ENOUGH_DATA || + self.mediaElement.readyState === HTMLMediaElement.HAVE_CURRENT_DATA, + "Ready state shall be HAVE_ENOUGH_DATA or HAVE_CURRENT_DATA"); - is(this.mediaElement.seeking, false, - "MediaElement is not seekable with MediaStream"); - ok(isNaN(this.mediaElement.startOffsetTime), - "Start offset time shall not be a number"); - is(this.mediaElement.loop, false, "Loop shall be false"); - is(this.mediaElement.preload, "", "Preload should not exist"); - is(this.mediaElement.src, "", "No src should be defined"); - is(this.mediaElement.currentSrc, "", - "Current src should still be an empty string"); + is(self.mediaElement.seekable.length, 0, + "Seekable length shall be zero"); + is(self.mediaElement.buffered.length, 0, + "Buffered length shall be zero"); - var timeUpdateCallback = () => { - if (this.mediaStream.currentTime > 0 && - this.mediaElement.currentTime > 0) { - this.mediaElement.removeEventListener('timeupdate', - timeUpdateCallback, false); - resolve(); - } - }; + is(self.mediaElement.seeking, false, + "MediaElement is not seekable with MediaStream"); + ok(isNaN(self.mediaElement.startOffsetTime), + "Start offset time shall not be a number"); + is(self.mediaElement.loop, false, "Loop shall be false"); + is(self.mediaElement.preload, "", "Preload should not exist"); + is(self.mediaElement.src, "", "No src should be defined"); + is(self.mediaElement.currentSrc, "", + "Current src should still be an empty string"); - // When timeupdate fires, we validate time has passed and move - // onto the success condition - this.mediaElement.addEventListener('timeupdate', timeUpdateCallback, - false); + var timeUpdateFired = false; - // If timeupdate doesn't fire in enough time, we fail the test - setTimeout(() => { - this.mediaElement.removeEventListener('timeupdate', - timeUpdateCallback, false); - reject(new Error("timeUpdate event never fired")); - }, TIMEUPDATE_TIMEOUT_LENGTH); + var timeUpdateCallback = function() { + if (self.mediaStream.currentTime > 0 && + self.mediaElement.currentTime > 0) { + timeUpdateFired = true; + self.mediaElement.removeEventListener('timeupdate', + timeUpdateCallback, false); + onSuccess(); + } }; - // Adds a listener intended to be fired when playback is available - // without further buffering. - this.mediaElement.addEventListener('canplaythrough', canPlayThroughCallback, - false); + // When timeupdate fires, we validate time has passed and move + // onto the success condition + self.mediaElement.addEventListener('timeupdate', timeUpdateCallback, + false); + + // If timeupdate doesn't fire in enough time, we fail the test + setTimeout(function() { + if (!timeUpdateFired) { + self.mediaElement.removeEventListener('timeupdate', + timeUpdateCallback, false); + onError("timeUpdate event never fired"); + } + }, TIMEUPDATE_TIMEOUT_LENGTH); + }; - // Hooks up the media stream to the media element and starts playing it - this.mediaElement.mozSrcObject = this.mediaStream; - this.mediaElement.play(); + // Adds a listener intended to be fired when playback is available + // without further buffering. + this.mediaElement.addEventListener('canplaythrough', canPlayThroughCallback, + false); + + // Hooks up the media stream to the media element and starts playing it + this.mediaElement.mozSrcObject = this.mediaStream; + this.mediaElement.play(); - // If canplaythrough doesn't fire in enough time, we fail the test - setTimeout(() => { - this.mediaElement.removeEventListener('canplaythrough', - canPlayThroughCallback, false); - reject(new Error("canplaythrough event never fired")); - }, CANPLAYTHROUGH_TIMEOUT_LENGTH); - }); + // If canplaythrough doesn't fire in enough time, we fail the test + setTimeout(function() { + if (!canPlayThroughFired) { + self.mediaElement.removeEventListener('canplaythrough', + canPlayThroughCallback, false); + onError("canplaythrough event never fired"); + } + }, CANPLAYTHROUGH_TIMEOUT_LENGTH); }, /** * Stops the media with the associated stream. * * Precondition: The media stream and element should both be actively * being played. */ - stopMediaElement : function() { + stopMediaElement : function MSP_stopMediaElement() { this.mediaElement.pause(); this.mediaElement.mozSrcObject = null; } } /** * This class is basically the same as MediaStreamPlayback except @@ -167,70 +181,69 @@ function LocalMediaStreamPlayback(mediaE LocalMediaStreamPlayback.prototype = Object.create(MediaStreamPlayback.prototype, { /** * Starts media with a media stream, runs it until a canplaythrough and * timeupdate event fires, and calls stop() on the stream. * * @param {Boolean} isResume specifies if this media element is being resumed * from a previous run + * @param {Function} onSuccess the success callback if the media element + * successfully fires ended on a stop() call + * on the stream + * @param {Function} onError the error callback if the media element fails + * to fire an ended callback on a stop() call + * on the stream */ playMediaWithStreamStop : { - value: function(isResume) { - return this.startMedia(isResume) - .then(() => this.stopStreamInMediaPlayback()) - .then(() => this.stopMediaElement()); + value: function (isResume, onSuccess, onError) { + var self = this; + + this.startMedia(isResume, function() { + self.stopStreamInMediaPlayback(function() { + self.stopMediaElement(); + onSuccess(); + }, onError); + }, onError); } }, /** * Stops the local media stream while it's currently in playback in * a media element. * * Precondition: The media stream and element should both be actively * being played. * + * @param {Function} onSuccess the success callback if the media element + * fires an ended event from stop() being called + * @param {Function} onError the error callback if the media element + * fails to fire an ended event from stop() being + * called */ stopStreamInMediaPlayback : { - value: function () { - return new Promise((resolve, reject) => { - /** - * Callback fired when the ended event fires when stop() is called on the - * stream. - */ - var endedCallback = () => { - this.mediaElement.removeEventListener('ended', endedCallback, false); - ok(true, "ended event successfully fired"); - resolve(); - }; + value: function (onSuccess, onError) { + var endedFired = false; + var self = this; - this.mediaElement.addEventListener('ended', endedCallback, false); - this.mediaStream.stop(); + /** + * Callback fired when the ended event fires when stop() is called on the + * stream. + */ + var endedCallback = function() { + endedFired = true; + self.mediaElement.removeEventListener('ended', endedCallback, false); + ok(true, "ended event successfully fired"); + onSuccess(); + }; - // If ended doesn't fire in enough time, then we fail the test - setTimeout(() => { - reject(new Error("ended event never fired")); - }, ENDED_TIMEOUT_LENGTH); - }); + this.mediaElement.addEventListener('ended', endedCallback, false); + this.mediaStream.stop(); + + // If ended doesn't fire in enough time, then we fail the test + setTimeout(function() { + if (!endedFired) { + onError("ended event never fired"); + } + }, ENDED_TIMEOUT_LENGTH); } } }); - -// haxx to prevent SimpleTest from failing at window.onload -function addLoadEvent() {} - -var scriptsReady = Promise.all([ - "/tests/SimpleTest/SimpleTest.js", - "head.js" -].map(script => { - var el = document.createElement("script"); - el.src = script; - document.head.appendChild(el); - return new Promise(r => el.onload = r); -})); - -function createHTML(options) { - return scriptsReady.then(() => realCreateHTML(options)); -} - -function runTest(f) { - return scriptsReady.then(() => runTestWhenReady(f)); -}
--- a/dom/media/tests/mochitest/mochitest.ini +++ b/dom/media/tests/mochitest/mochitest.ini @@ -1,17 +1,16 @@ [DEFAULT] # strictContentSandbox - bug 1042735, Android 2.3 - bug 981881 skip-if = (os == 'win' && strictContentSandbox) || android_version == '10' support-files = head.js constraints.js dataChannel.js mediaStreamPlayback.js - network.js nonTrickleIce.js pc.js templates.js NetworkPreparationChromeScript.js blacksilence.js turnConfig.js [test_dataChannel_basicAudio.html] @@ -40,17 +39,16 @@ skip-if = buildapp == 'b2g' || toolkit = skip-if = buildapp == 'b2g' || toolkit == 'android' # no windowshare on b2g/android [test_getUserMedia_basicVideoAudio.html] skip-if = (toolkit == 'gonk' && debug) # debug-only failure, turned an intermittent (bug 962579) into a permanant orange [test_getUserMedia_constraints.html] skip-if = toolkit == 'gonk' || toolkit == 'android' # Bug 907352, backwards-compatible behavior on mobile only [test_getUserMedia_constraints_mobile.html] skip-if = toolkit != 'gonk' && toolkit != 'android' # Bug 907352, backwards-compatible behavior on mobile only [test_getUserMedia_exceptions.html] -[test_getUserMedia_callbacks.html] [test_getUserMedia_gumWithinGum.html] [test_getUserMedia_playAudioTwice.html] [test_getUserMedia_playVideoAudioTwice.html] skip-if = (toolkit == 'gonk' && debug) # debug-only failure; bug 926558 [test_getUserMedia_playVideoTwice.html] [test_getUserMedia_stopAudioStream.html] [test_getUserMedia_stopAudioStreamWithFollowupAudio.html] [test_getUserMedia_stopVideoAudioStream.html] @@ -109,18 +107,16 @@ skip-if = toolkit == 'gonk' # b2g (Bug 1 [test_peerConnection_offerRequiresReceiveAudio.html] skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g) [test_peerConnection_offerRequiresReceiveVideo.html] skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g) [test_peerConnection_offerRequiresReceiveVideoAudio.html] skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g) [test_peerConnection_promiseSendOnly.html] skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g) -[test_peerConnection_callbacks.html] -skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g) [test_peerConnection_replaceTrack.html] skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g) [test_peerConnection_syncSetDescription.html] skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g) [test_peerConnection_setLocalAnswerInHaveLocalOffer.html] skip-if = toolkit == 'gonk' # b2g (Bug 1059867) [test_peerConnection_setLocalAnswerInStable.html] skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
deleted file mode 100644 --- a/dom/media/tests/mochitest/network.js +++ /dev/null @@ -1,121 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -/** - * Query function for determining if any IP address is available for - * generating SDP. - * - * @return false if required additional network setup. - */ -function isNetworkReady() { - // for gonk platform - if ("nsINetworkInterfaceListService" in SpecialPowers.Ci) { - var listService = SpecialPowers.Cc["@mozilla.org/network/interface-list-service;1"] - .getService(SpecialPowers.Ci.nsINetworkInterfaceListService); - var itfList = listService.getDataInterfaceList( - SpecialPowers.Ci.nsINetworkInterfaceListService.LIST_NOT_INCLUDE_MMS_INTERFACES | - SpecialPowers.Ci.nsINetworkInterfaceListService.LIST_NOT_INCLUDE_SUPL_INTERFACES | - SpecialPowers.Ci.nsINetworkInterfaceListService.LIST_NOT_INCLUDE_IMS_INTERFACES | - SpecialPowers.Ci.nsINetworkInterfaceListService.LIST_NOT_INCLUDE_DUN_INTERFACES); - var num = itfList.getNumberOfInterface(); - for (var i = 0; i < num; i++) { - var ips = {}; - var prefixLengths = {}; - var length = itfList.getInterface(i).getAddresses(ips, prefixLengths); - - for (var j = 0; j < length; j++) { - var ip = ips.value[j]; - // skip IPv6 address until bug 797262 is implemented - if (ip.indexOf(":") < 0) { - safeInfo("Network interface is ready with address: " + ip); - return true; - } - } - } - // ip address is not available - safeInfo("Network interface is not ready, required additional network setup"); - return false; - } - safeInfo("Network setup is not required"); - return true; -} - -/** - * Network setup utils for Gonk - * - * @return {object} providing functions for setup/teardown data connection - */ -function getNetworkUtils() { - var url = SimpleTest.getTestFileURL("NetworkPreparationChromeScript.js"); - var script = SpecialPowers.loadChromeScript(url); - - var utils = { - /** - * Utility for setting up data connection. - * - * @param aCallback callback after data connection is ready. - */ - prepareNetwork: function() { - return new Promise(resolve => { - script.addMessageListener('network-ready', () => { - info("Network interface is ready"); - resolve(); - }); - info("Setting up network interface"); - script.sendAsyncMessage("prepare-network", true); - }); - }, - /** - * Utility for tearing down data connection. - * - * @param aCallback callback after data connection is closed. - */ - tearDownNetwork: function() { - if (!isNetworkReady()) { - info("No network to tear down"); - return Promise.resolve(); - } - return new Promise(resolve => { - script.addMessageListener('network-disabled', message => { - info("Network interface torn down"); - script.destroy(); - resolve(); - }); - info("Tearing down network interface"); - script.sendAsyncMessage("network-cleanup", true); - }); - } - }; - - return utils; -} - -/** - * Setup network on Gonk if needed and execute test once network is up - * - */ -function startNetworkAndTest() { - if (isNetworkReady()) { - return Promise.resolve(); - } - var utils = getNetworkUtils(); - // Trigger network setup to obtain IP address before creating any PeerConnection. - return utils.prepareNetwork(); -} - -/** - * A wrapper around SimpleTest.finish() to handle B2G network teardown - */ -function networkTestFinished() { - var p; - if ("nsINetworkInterfaceListService" in SpecialPowers.Ci) { - var utils = getNetworkUtils(); - p = utils.tearDownNetwork(); - } else { - p = Promise.resolve(); - } - return p.then(() => finish()); -}
--- a/dom/media/tests/mochitest/nonTrickleIce.js +++ b/dom/media/tests/mochitest/nonTrickleIce.js @@ -1,60 +1,130 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ function makeOffererNonTrickle(chain) { chain.replace('PC_LOCAL_SETUP_ICE_HANDLER', [ - function PC_LOCAL_SETUP_NOTRICKLE_ICE_HANDLER(test) { - // We need to install this callback before calling setLocalDescription - // otherwise we might miss callbacks - test.pcLocal.setupIceCandidateHandler(test, () => {}); - // We ignore ICE candidates because we want the full offer - } + ['PC_LOCAL_SETUP_NOTRICKLE_ICE_HANDLER', + function (test) { + test.pcLocalWaitingForEndOfTrickleIce = false; + // We need to install this callback before calling setLocalDescription + // otherwise we might miss callbacks + test.pcLocal.setupIceCandidateHandler(test, function () { + // We ignore ICE candidates because we want the full offer + } , function (label) { + if (test.pcLocalWaitingForEndOfTrickleIce) { + // This callback is needed for slow environments where ICE + // trickling has not finished before the other side needs the + // full SDP. In this case, this call to test.next() will complete + // the PC_REMOTE_WAIT_FOR_OFFER step (see below). + info("Looks like we were still waiting for Trickle to finish"); + // TODO replace this with a Promise + test.next(); + } + }); + // We can't wait for trickle to finish here as it will only start once + // we have called setLocalDescription in the next step + test.next(); + } + ] ]); chain.replace('PC_REMOTE_GET_OFFER', [ - function PC_REMOTE_GET_FULL_OFFER(test) { - return test.pcLocal.endOfTrickleIce.then(() => { + ['PC_REMOTE_WAIT_FOR_OFFER', + function (test) { + if (test.pcLocal.endOfTrickleIce) { + info("Trickle ICE finished already"); + test.next(); + } else { + info("Waiting for trickle ICE to finish"); + test.pcLocalWaitingForEndOfTrickleIce = true; + // In this case we rely on the callback from + // PC_LOCAL_SETUP_NOTRICKLE_ICE_HANDLER above to proceed to the next + // step once trickle is finished. + } + } + ], + ['PC_REMOTE_GET_FULL_OFFER', + function (test) { test._local_offer = test.pcLocal.localDescription; test._offer_constraints = test.pcLocal.constraints; test._offer_options = test.pcLocal.offerOptions; - }); - } + test.next(); + } + ] ]); chain.insertAfter('PC_REMOTE_SANE_REMOTE_SDP', [ - function PC_REMOTE_REQUIRE_REMOTE_SDP_CANDIDATES(test) { - info("test.pcLocal.localDescription.sdp: " + JSON.stringify(test.pcLocal.localDescription.sdp)); - info("test._local_offer.sdp" + JSON.stringify(test._local_offer.sdp)); - ok(!test.localRequiresTrickleIce, "Local does NOT require trickle"); - ok(test._local_offer.sdp.contains("a=candidate"), "offer has ICE candidates") - ok(test._local_offer.sdp.contains("a=end-of-candidates"), "offer has end-of-candidates"); - } + ['PC_REMOTE_REQUIRE_REMOTE_SDP_CANDIDATES', + function (test) { + info("test.pcLocal.localDescription.sdp: " + JSON.stringify(test.pcLocal.localDescription.sdp)); + info("test._local_offer.sdp" + JSON.stringify(test._local_offer.sdp)); + ok(!test.localRequiresTrickleIce, "Local does NOT require trickle"); + ok(test._local_offer.sdp.contains("a=candidate"), "offer has ICE candidates") + // TODO check for a=end-of-candidates once implemented + test.next(); + } + ] ]); } function makeAnswererNonTrickle(chain) { chain.replace('PC_REMOTE_SETUP_ICE_HANDLER', [ - function PC_REMOTE_SETUP_NOTRICKLE_ICE_HANDLER(test) { - // We need to install this callback before calling setLocalDescription - // otherwise we might miss callbacks - test.pcRemote.setupIceCandidateHandler(test, () => {}); - // We ignore ICE candidates because we want the full offer - } + ['PC_REMOTE_SETUP_NOTRICKLE_ICE_HANDLER', + function (test) { + test.pcRemoteWaitingForEndOfTrickleIce = false; + // We need to install this callback before calling setLocalDescription + // otherwise we might miss callbacks + test.pcRemote.setupIceCandidateHandler(test, function () { + // We ignore ICE candidates because we want the full answer + }, function (label) { + if (test.pcRemoteWaitingForEndOfTrickleIce) { + // This callback is needed for slow environments where ICE + // trickling has not finished before the other side needs the + // full SDP. In this case this callback will call the step after + // PC_LOCAL_WAIT_FOR_ANSWER + info("Looks like we were still waiting for Trickle to finish"); + // TODO replace this with a Promise + test.next(); + } + }); + // We can't wait for trickle to finish here as it will only start once + // we have called setLocalDescription in the next step + test.next(); + } + ] ]); chain.replace('PC_LOCAL_GET_ANSWER', [ - function PC_LOCAL_GET_FULL_ANSWER(test) { - return test.pcRemote.endOfTrickleIce.then(() => { + ['PC_LOCAL_WAIT_FOR_ANSWER', + function (test) { + if (test.pcRemote.endOfTrickleIce) { + info("Trickle ICE finished already"); + test.next(); + } else { + info("Waiting for trickle ICE to finish"); + test.pcRemoteWaitingForEndOfTrickleIce = true; + // In this case we rely on the callback from + // PC_REMOTE_SETUP_NOTRICKLE_ICE_HANDLER above to proceed to the next + // step once trickle is finished. + } + } + ], + ['PC_LOCAL_GET_FULL_ANSWER', + function (test) { test._remote_answer = test.pcRemote.localDescription; test._answer_constraints = test.pcRemote.constraints; - }); - } + test.next(); + } + ] ]); chain.insertAfter('PC_LOCAL_SANE_REMOTE_SDP', [ - function PC_LOCAL_REQUIRE_REMOTE_SDP_CANDIDATES(test) { - info("test.pcRemote.localDescription.sdp: " + JSON.stringify(test.pcRemote.localDescription.sdp)); - info("test._remote_answer.sdp" + JSON.stringify(test._remote_answer.sdp)); - ok(!test.remoteRequiresTrickleIce, "Remote does NOT require trickle"); - ok(test._remote_answer.sdp.contains("a=candidate"), "answer has ICE candidates") - ok(test._remote_answer.sdp.contains("a=end-of-candidates"), "answer has end-of-candidates"); - } + ['PC_LOCAL_REQUIRE_REMOTE_SDP_CANDIDATES', + function (test) { + info("test.pcRemote.localDescription.sdp: " + JSON.stringify(test.pcRemote.localDescription.sdp)); + info("test._remote_answer.sdp" + JSON.stringify(test._remote_answer.sdp)); + ok(!test.remoteRequiresTrickleIce, "Remote does NOT require trickle"); + ok(test._remote_answer.sdp.contains("a=candidate"), "answer has ICE candidates") + // TODO check for a=end-of-candidates once implemented + test.next(); + } + ] ]); }
--- a/dom/media/tests/mochitest/pc.js +++ b/dom/media/tests/mochitest/pc.js @@ -24,91 +24,355 @@ const signalingStateTransitions = { "have-local-offer": ["have-remote-pranswer", "stable", "closed", "have-local-offer"], "have-remote-pranswer": ["stable", "closed", "have-remote-pranswer"], "have-remote-offer": ["have-local-pranswer", "stable", "closed", "have-remote-offer"], "have-local-pranswer": ["stable", "closed", "have-local-pranswer"], "closed": [] } /** + * This class mimics a state machine and handles a list of commands by + * executing them synchronously. + * + * @constructor + * @param {object} framework + * A back reference to the framework which makes use of the class. It's + * getting passed in as parameter to each command callback. + * @param {Array[]} [commandList=[]] + * Default commands to set during initialization + */ +function CommandChain(framework, commandList) { + this._framework = framework; + + this._commands = commandList || [ ]; + this._current = 0; + + this.onFinished = null; +} + +CommandChain.prototype = { + + /** + * Returns the index of the current command of the chain + * + * @returns {number} Index of the current command + */ + get current() { + return this._current; + }, + + /** + * Checks if the chain has already processed all the commands + * + * @returns {boolean} True, if all commands have been processed + */ + get finished() { + return this._current === this._commands.length; + }, + + /** + * Returns the assigned commands of the chain. + * + * @returns {Array[]} Commands of the chain + */ + get commands() { + return this._commands; + }, + + /** + * Sets new commands for the chain. All existing commands will be replaced. + * + * @param {Array[]} commands + * List of commands + */ + set commands(commands) { + this._commands = commands; + }, + + /** + * Execute the next command in the chain. + */ + executeNext : function () { + var self = this; + + function _executeNext() { + if (!self.finished) { + var step = self._commands[self._current]; + self._current++; + + self.currentStepLabel = step[0]; + info("Run step: " + self.currentStepLabel); + step[1](self._framework); // Execute step + } + else if (typeof(self.onFinished) === 'function') { + self.onFinished(); + } + } + + // To prevent building up the stack we have to execute the next + // step asynchronously + window.setTimeout(_executeNext, 0); + }, + + /** + * Add new commands to the end of the chain + * + * @param {Array[]} commands + * List of commands + */ + append: function (commands) { + this._commands = this._commands.concat(commands); + }, + + /** + * Returns the index of the specified command in the chain. + * + * @param {string} id + * Identifier of the command + * @returns {number} Index of the command + */ + indexOf: function (id) { + for (var i = 0; i < this._commands.length; i++) { + if (this._commands[i][0] === id) { + return i; + } + } + + return -1; + }, + + /** + * Inserts the new commands after the specified command. + * + * @param {string} id + * Identifier of the command + * @param {Array[]} commands + * List of commands + */ + insertAfter: function (id, commands) { + var index = this.indexOf(id); + + if (index > -1) { + var tail = this.removeAfter(id); + + this.append(commands); + this.append(tail); + } + }, + + /** + * Inserts the new commands before the specified command. + * + * @param {string} id + * Identifier of the command + * @param {Array[]} commands + * List of commands + */ + insertBefore: function (id, commands) { + var index = this.indexOf(id); + + if (index > -1) { + var tail = this.removeAfter(id); + var object = this.remove(id); + + this.append(commands); + this.append(object); + this.append(tail); + } + }, + + /** + * Removes the specified command + * + * @param {string} id + * Identifier of the command + * @returns {object[]} Removed command + */ + remove : function (id) { + return this._commands.splice(this.indexOf(id), 1); + }, + + /** + * Removes all commands after the specified one. + * + * @param {string} id + * Identifier of the command + * @returns {object[]} Removed commands + */ + removeAfter : function (id) { + var index = this.indexOf(id); + + if (index > -1) { + return this._commands.splice(index + 1); + } + + return null; + }, + + /** + * Removes all commands before the specified one. + * + * @param {string} id + * Identifier of the command + * @returns {object[]} Removed commands + */ + removeBefore : function (id) { + var index = this.indexOf(id); + + if (index > -1) { + return this._commands.splice(0, index); + } + + return null; + }, + + /** + * Replaces a single command. + * + * @param {string} id + * Identifier of the command to be replaced + * @param {Array[]} commands + * List of commands + * @returns {object[]} Removed commands + */ + replace : function (id, commands) { + this.insertBefore(id, commands); + return this.remove(id); + }, + + /** + * Replaces all commands after the specified one. + * + * @param {string} id + * Identifier of the command + * @returns {object[]} Removed commands + */ + replaceAfter : function (id, commands) { + var oldCommands = this.removeAfter(id); + this.append(commands); + + return oldCommands; + }, + + /** + * Replaces all commands before the specified one. + * + * @param {string} id + * Identifier of the command + * @returns {object[]} Removed commands + */ + replaceBefore : function (id, commands) { + var oldCommands = this.removeBefore(id); + this.insertBefore(id, commands); + + return oldCommands; + }, + + /** + * Remove all commands whose identifiers match the specified regex. + * + * @param {regex} id_match + * Regular expression to match command identifiers. + */ + filterOut : function (id_match) { + for (var i = this._commands.length - 1; i >= 0; i--) { + if (id_match.test(this._commands[i][0])) { + this._commands.splice(i, 1); + } + } + } +}; + +/** * This class provides a state checker for media elements which store * a media stream to check for media attribute state and events fired. * When constructed by a caller, an object instance is created with * a media element, event state checkers for canplaythrough, timeupdate, and * time changing on the media element and stream. * * @param {HTMLMediaElement} element the media element being analyzed */ function MediaElementChecker(element) { this.element = element; this.canPlayThroughFired = false; this.timeUpdateFired = false; this.timePassed = false; - var elementId = this.element.getAttribute('id'); + var self = this; + var elementId = self.element.getAttribute('id'); // When canplaythrough fires, we track that it's fired and remove the // event listener. - var canPlayThroughCallback = () => { + var canPlayThroughCallback = function() { info('canplaythrough fired for media element ' + elementId); - this.canPlayThroughFired = true; - this.element.removeEventListener('canplaythrough', canPlayThroughCallback, + self.canPlayThroughFired = true; + self.element.removeEventListener('canplaythrough', canPlayThroughCallback, false); }; // When timeupdate fires, we track that it's fired and check if time // has passed on the media stream and media element. - var timeUpdateCallback = () => { - this.timeUpdateFired = true; + var timeUpdateCallback = function() { + self.timeUpdateFired = true; info('timeupdate fired for media element ' + elementId); // If time has passed, then track that and remove the timeupdate event // listener. if(element.mozSrcObject && element.mozSrcObject.currentTime > 0 && element.currentTime > 0) { info('time passed for media element ' + elementId); - this.timePassed = true; - this.element.removeEventListener('timeupdate', timeUpdateCallback, + self.timePassed = true; + self.element.removeEventListener('timeupdate', timeUpdateCallback, false); } }; element.addEventListener('canplaythrough', canPlayThroughCallback, false); element.addEventListener('timeupdate', timeUpdateCallback, false); } MediaElementChecker.prototype = { /** * Waits until the canplaythrough & timeupdate events to fire along with * ensuring time has passed on the stream and media element. + * + * @param {Function} onSuccess the success callback when media flow is + * established */ - waitForMediaFlow: function() { - var elementId = this.element.getAttribute('id'); + waitForMediaFlow : function MEC_WaitForMediaFlow(onSuccess) { + var self = this; + var elementId = self.element.getAttribute('id'); info('Analyzing element: ' + elementId); - return waitUntil(() => this.canPlayThroughFired && this.timeUpdateFired && this.timePassed) - .then(() => ok(true, 'Media flowing for ' + elementId)); + if(self.canPlayThroughFired && self.timeUpdateFired && self.timePassed) { + ok(true, 'Media flowing for ' + elementId); + onSuccess(); + } else { + setTimeout(function() { + self.waitForMediaFlow(onSuccess); + }, 100); + } }, /** * Checks if there is no media flow present by checking that the ready * state of the media element is HAVE_METADATA. */ - checkForNoMediaFlow: function() { + checkForNoMediaFlow : function MEC_CheckForNoMediaFlow() { ok(this.element.readyState === HTMLMediaElement.HAVE_METADATA, 'Media element has a ready state of HAVE_METADATA'); } }; /** * Only calls info() if SimpleTest.info() is available */ function safeInfo(message) { - if (typeof info === "function") { + if (typeof(info) === "function") { info(message); } } // Also remove mode 0 if it's offered // Note, we don't bother removing the fmtp lines, which makes a good test // for some SDP parsing issues. function removeVP8(sdp) { @@ -117,16 +381,138 @@ function removeVP8(sdp) { updated_sdp = updated_sdp.replace("RTP/SAVPF 120 126\r\n","RTP/SAVPF 126\r\n"); updated_sdp = updated_sdp.replace("a=rtcp-fb:120 nack\r\n",""); updated_sdp = updated_sdp.replace("a=rtcp-fb:120 nack pli\r\n",""); updated_sdp = updated_sdp.replace("a=rtcp-fb:120 ccm fir\r\n",""); return updated_sdp; } /** + * Query function for determining if any IP address is available for + * generating SDP. + * + * @return false if required additional network setup. + */ +function isNetworkReady() { + // for gonk platform + if ("nsINetworkInterfaceListService" in SpecialPowers.Ci) { + var listService = SpecialPowers.Cc["@mozilla.org/network/interface-list-service;1"] + .getService(SpecialPowers.Ci.nsINetworkInterfaceListService); + var itfList = listService.getDataInterfaceList( + SpecialPowers.Ci.nsINetworkInterfaceListService.LIST_NOT_INCLUDE_MMS_INTERFACES | + SpecialPowers.Ci.nsINetworkInterfaceListService.LIST_NOT_INCLUDE_SUPL_INTERFACES | + SpecialPowers.Ci.nsINetworkInterfaceListService.LIST_NOT_INCLUDE_IMS_INTERFACES | + SpecialPowers.Ci.nsINetworkInterfaceListService.LIST_NOT_INCLUDE_DUN_INTERFACES); + var num = itfList.getNumberOfInterface(); + for (var i = 0; i < num; i++) { + var ips = {}; + var prefixLengths = {}; + var length = itfList.getInterface(i).getAddresses(ips, prefixLengths); + + for (var j = 0; j < length; j++) { + var ip = ips.value[j]; + // skip IPv6 address until bug 797262 is implemented + if (ip.indexOf(":") < 0) { + safeInfo("Network interface is ready with address: " + ip); + return true; + } + } + } + // ip address is not available + safeInfo("Network interface is not ready, required additional network setup"); + return false; + } + safeInfo("Network setup is not required"); + return true; +} + +/** + * Network setup utils for Gonk + * + * @return {object} providing functions for setup/teardown data connection + */ +function getNetworkUtils() { + var url = SimpleTest.getTestFileURL("NetworkPreparationChromeScript.js"); + var script = SpecialPowers.loadChromeScript(url); + + var utils = { + /** + * Utility for setting up data connection. + * + * @param aCallback callback after data connection is ready. + */ + prepareNetwork: function(onSuccess) { + script.addMessageListener('network-ready', function (message) { + info("Network interface is ready"); + onSuccess(); + }); + info("Setting up network interface"); + script.sendAsyncMessage("prepare-network", true); + }, + /** + * Utility for tearing down data connection. + * + * @param aCallback callback after data connection is closed. + */ + tearDownNetwork: function(onSuccess, onFailure) { + if (isNetworkReady()) { + script.addMessageListener('network-disabled', function (message) { + info("Network interface torn down"); + script.destroy(); + onSuccess(); + }); + info("Tearing down network interface"); + script.sendAsyncMessage("network-cleanup", true); + } else { + info("No network to tear down"); + onFailure(); + } + } + }; + + return utils; +} + +/** + * Setup network on Gonk if needed and execute test once network is up + * + */ +function startNetworkAndTest(onSuccess) { + if (!isNetworkReady()) { + SimpleTest.waitForExplicitFinish(); + var utils = getNetworkUtils(); + // Trigger network setup to obtain IP address before creating any PeerConnection. + utils.prepareNetwork(onSuccess); + } else { + onSuccess(); + } +} + +/** + * A wrapper around SimpleTest.finish() to handle B2G network teardown + */ +function networkTestFinished() { + if ("nsINetworkInterfaceListService" in SpecialPowers.Ci) { + var utils = getNetworkUtils(); + utils.tearDownNetwork(SimpleTest.finish, SimpleTest.finish); + } else { + SimpleTest.finish(); + } +} + +/** + * A wrapper around runTest() which handles B2G network setup and teardown + */ +function runNetworkTest(testFunction) { + startNetworkAndTest(function() { + runTest(testFunction); + }); +} + +/** * This class handles tests for peer connections. * * @constructor * @param {object} [options={}] * Optional options for the peer connection test * @param {object} [options.commands=commandsPeerConnection] * Commands to run for the test * @param {bool} [options.is_local=true] @@ -180,337 +566,637 @@ function PeerConnectionTest(options) { // Create command chain instance and assign default commands this.chain = new CommandChain(this, options.commands); if (!options.is_local) { this.chain.filterOut(/^PC_LOCAL/); } if (!options.is_remote) { this.chain.filterOut(/^PC_REMOTE/); } -} -/** TODO: consider removing this dependency on timeouts */ -function timerGuard(p, time, message) { - return Promise.race([ - p, - wait(time).then(() => { - throw new Error('timeout after ' + (time / 1000) + 's: ' + message); - }) - ]); + var self = this; + this.chain.onFinished = function () { + self.teardown(); + }; } /** * Closes the peer connection if it is active * * @param {Function} onSuccess * Callback to execute when the peer connection has been closed successfully */ -PeerConnectionTest.prototype.closePC = function() { +PeerConnectionTest.prototype.closePC = function PCT_closePC(onSuccess) { info("Closing peer connections"); - var closeIt = pc => { - if (!pc || pc.signalingState === "closed") { - return Promise.resolve(); + var self = this; + var closeTimeout = null; + var waitingForLocal = false; + var waitingForRemote = false; + var everythingClosed = false; + + function verifyClosed() { + if ((self.waitingForLocal || self.waitingForRemote) || + (self.pcLocal && (self.pcLocal.signalingState !== "closed")) || + (self.pcRemote && (self.pcRemote.signalingState !== "closed"))) { + info("still waiting for closure"); } + else if (!everythingClosed) { + info("No closure pending"); + if (self.pcLocal) { + is(self.pcLocal.signalingState, "closed", "pcLocal is in 'closed' state"); + } + if (self.pcRemote) { + is(self.pcRemote.signalingState, "closed", "pcRemote is in 'closed' state"); + } + clearTimeout(closeTimeout); + everythingClosed = true; + onSuccess(); + } + } + + function signalingstatechangeLocalClose(e) { + info("'signalingstatechange' event received"); + is(e.target.signalingState, "closed", "signalingState is closed"); + self.waitingForLocal = false; + verifyClosed(); + } - return new Promise(resolve => { - pc.onsignalingstatechange = e => { - is(e.target.signalingState, "closed", "signalingState is closed"); - resolve(); - }; - pc.close(); - }); - }; + function signalingstatechangeRemoteClose(e) { + info("'signalingstatechange' event received"); + is(e.target.signalingState, "closed", "signalingState is closed"); + self.waitingForRemote = false; + verifyClosed(); + } - return timerGuard(Promise.all([ - closeIt(this.pcLocal), - closeIt(this.pcRemote) - ]), 60000, "failed to close peer connection"); + function closeEverything() { + if ((self.pcLocal) && (self.pcLocal.signalingState !== "closed")) { + info("Closing pcLocal"); + self.pcLocal.onsignalingstatechange = signalingstatechangeLocalClose; + self.waitingForLocal = true; + self.pcLocal.close(); + } + if ((self.pcRemote) && (self.pcRemote.signalingState !== "closed")) { + info("Closing pcRemote"); + self.pcRemote.onsignalingstatechange = signalingstatechangeRemoteClose; + self.waitingForRemote = true; + self.pcRemote.close(); + } + // give the signals handlers time to fire + setTimeout(verifyClosed, 1000); + } + + closeTimeout = setTimeout(function() { + var closed = ((self.pcLocal && (self.pcLocal.signalingState === "closed")) && + (self.pcRemote && (self.pcRemote.signalingState === "closed"))); + ok(closed, "Closing PeerConnections timed out"); + // it is not a success, but the show must go on + onSuccess(); + }, 60000); + + closeEverything(); }; /** * Close the open data channels, followed by the underlying peer connection + * + * @param {Function} onSuccess + * Callback to execute when all connections have been closed */ -PeerConnectionTest.prototype.close = function() { - var allChannels = (this.pcLocal || this.pcRemote).dataChannels; - return timerGuard( - Promise.all(allChannels.map((channel, i) => this.closeDataChannels(i))), - 60000, "failed to close data channels") - .then(() => this.closePC()); +PeerConnectionTest.prototype.close = function PCT_close(onSuccess) { + var self = this; + var pendingDcClose = [] + var closeTimeout = null; + + info("PeerConnectionTest.close() called"); + + function _closePeerConnection() { + info("Now closing PeerConnection"); + self.closePC.call(self, onSuccess); + } + + function _closePeerConnectionCallback(index) { + info("_closePeerConnection called with index " + index); + var pos = pendingDcClose.indexOf(index); + if (pos != -1) { + pendingDcClose.splice(pos, 1); + } + else { + info("_closePeerConnection index " + index + " is missing from pendingDcClose: " + pendingDcClose); + } + if (pendingDcClose.length === 0) { + clearTimeout(closeTimeout); + _closePeerConnection(); + } + } + + var myDataChannels = null; + if (self.pcLocal) { + myDataChannels = self.pcLocal.dataChannels; + } + else if (self.pcRemote) { + myDataChannels = self.pcRemote.dataChannels; + } + var length = myDataChannels.length; + for (var i = 0; i < length; i++) { + var dataChannel = myDataChannels[i]; + if (dataChannel.readyState !== "closed") { + pendingDcClose.push(i); + self.closeDataChannels(i, _closePeerConnectionCallback); + } + } + if (pendingDcClose.length === 0) { + _closePeerConnection(); + } + else { + closeTimeout = setTimeout(function() { + ok(false, "Failed to properly close data channels: " + + pendingDcClose); + _closePeerConnection(); + }, 60000); + } }; /** * Close the specified data channels * * @param {Number} index * Index of the data channels to close on both sides + * @param {Function} onSuccess + * Callback to execute when the data channels has been closed */ -PeerConnectionTest.prototype.closeDataChannels = function(index) { +PeerConnectionTest.prototype.closeDataChannels = function PCT_closeDataChannels(index, onSuccess) { info("closeDataChannels called with index: " + index); var localChannel = null; if (this.pcLocal) { localChannel = this.pcLocal.dataChannels[index]; } var remoteChannel = null; if (this.pcRemote) { remoteChannel = this.pcRemote.dataChannels[index]; } - // We need to setup all the close listeners before calling close - var setupClosePromise = channel => { - if (!channel) { - return Promise.resolve(); - } - return new Promise(resolve => { - channel.onclose = () => { - is(channel.readyState, "closed", name + " channel " + index + " closed"); - resolve(); - }; - }); - }; + var self = this; + var wait = false; + var pollingMode = false; + var everythingClosed = false; + var verifyInterval = null; + var remoteCloseTimer = null; - // make sure to setup close listeners before triggering any actions - var allClosed = Promise.all([ - setupClosePromise(localChannel), - setupClosePromise(remoteChannel) - ]); - var complete = timerGuard(allClosed, 60000, "failed to close data channel pair"); + function _allChannelsAreClosed() { + var ret = null; + if (localChannel) { + ret = (localChannel.readyState === "closed"); + } + if (remoteChannel) { + if (ret !== null) { + ret = (ret && (remoteChannel.readyState === "closed")); + } + else { + ret = (remoteChannel.readyState === "closed"); + } + } + return ret; + } - // triggering close on one side should suffice - if (remoteChannel) { - remoteChannel.close(); - } else if (localChannel) { - localChannel.close(); + function verifyClosedChannels() { + if (everythingClosed) { + // safety protection against events firing late + return; + } + if (_allChannelsAreClosed()) { + ok(true, "DataChannel(s) have reached 'closed' state for data channel " + index); + if (remoteCloseTimer !== null) { + clearTimeout(remoteCloseTimer); + } + if (verifyInterval !== null) { + clearInterval(verifyInterval); + } + everythingClosed = true; + onSuccess(index); + } + else { + info("Still waiting for DataChannel closure"); + } } - return complete; + if ((localChannel) && (localChannel.readyState !== "closed")) { + // in case of steeplechase there is no far end, so we can only poll + if (remoteChannel) { + remoteChannel.onclose = function () { + is(remoteChannel.readyState, "closed", "remoteChannel is in state 'closed'"); + verifyClosedChannels(); + }; + } + else { + pollingMode = true; + verifyInterval = setInterval(verifyClosedChannels, 1000); + } + + localChannel.close(); + wait = true; + } + if ((remoteChannel) && (remoteChannel.readyState !== "closed")) { + if (localChannel) { + localChannel.onclose = function () { + is(localChannel.readyState, "closed", "localChannel is in state 'closed'"); + verifyClosedChannels(); + }; + + // Apparently we are running a local test which has both ends of the + // data channel locally available, so by default lets wait for the + // remoteChannel.onclose handler from above to confirm closure on both + // ends. + remoteCloseTimer = setTimeout(function() { + todo(false, "localChannel.close() did not resulted in close signal on remote side"); + remoteChannel.close(); + verifyClosedChannels(); + }, 30000); + } + else { + pollingMode = true; + verifyTimer = setInterval(verifyClosedChannels, 1000); + + remoteChannel.close(); + } + + wait = true; + } + + if (!wait) { + onSuccess(index); + } +}; + + +/** + * Wait for the initial data channel to get into the open state + * + * @param {PeerConnectionWrapper} peer + * The peer connection wrapper to run the command on + * @param {Function} onSuccess + * Callback when the creation was successful + */ +PeerConnectionTest.prototype.waitForInitialDataChannel = + function PCT_waitForInitialDataChannel(peer, onSuccess, onFailure) { + var dcConnectionTimeout = null; + var dcOpened = false; + + function dataChannelConnected(channel) { + // in case the switch statement below had called onSuccess already we + // don't want to call it again + if (!dcOpened) { + clearTimeout(dcConnectionTimeout); + is(channel.readyState, "open", peer + " dataChannels[0] switched to state: 'open'"); + dcOpened = true; + onSuccess(); + } else { + info("dataChannelConnected() called, but data channel was open already"); + } + } + + // TODO: drno: convert dataChannels into an object and make + // registerDataChannelOpenEvent a generic function + if (peer == this.pcLocal) { + peer.dataChannels[0].onopen = dataChannelConnected; + } else { + peer.registerDataChannelOpenEvents(dataChannelConnected); + } + + if (peer.dataChannels.length >= 1) { + // snapshot of the live value as it might change during test execution + const readyState = peer.dataChannels[0].readyState; + switch (readyState) { + case "open": { + is(readyState, "open", peer + " dataChannels[0] is already in state: 'open'"); + dcOpened = true; + onSuccess(); + break; + } + case "connecting": { + is(readyState, "connecting", peer + " dataChannels[0] is in state: 'connecting'"); + if (onFailure) { + dcConnectionTimeout = setTimeout(function () { + is(peer.dataChannels[0].readyState, "open", peer + " timed out while waiting for dataChannels[0] to open"); + onFailure(); + }, 60000); + } + break; + } + default: { + ok(false, "dataChannels[0] is in unexpected state " + readyState); + if (onFailure) { + onFailure() + } + } + } + } }; /** * Send data (message or blob) to the other peer * * @param {String|Blob} data * Data to send to the other peer. For Blobs the MIME type will be lost. + * @param {Function} onSuccess + * Callback to execute when data has been sent * @param {Object} [options={ }] * Options to specify the data channels to be used * @param {DataChannelWrapper} [options.sourceChannel=pcLocal.dataChannels[length - 1]] * Data channel to use for sending the message * @param {DataChannelWrapper} [options.targetChannel=pcRemote.dataChannels[length - 1]] * Data channel to use for receiving the message */ -PeerConnectionTest.prototype.send = function(data, options) { +PeerConnectionTest.prototype.send = function PCT_send(data, onSuccess, options) { options = options || { }; var source = options.sourceChannel || this.pcLocal.dataChannels[this.pcLocal.dataChannels.length - 1]; var target = options.targetChannel || this.pcRemote.dataChannels[this.pcRemote.dataChannels.length - 1]; - return new Promise(resolve => { - // Register event handler for the target channel - target.onmessage = e => { - resolve({ channel: target, data: e.data }); - }; + // Register event handler for the target channel + target.onmessage = function (recv_data) { + onSuccess(target, recv_data); + }; - source.send(data); - }); + source.send(data); }; /** * Create a data channel * * @param {Dict} options * Options for the data channel (see nsIPeerConnection) + * @param {Function} onSuccess + * Callback when the creation was successful */ -PeerConnectionTest.prototype.createDataChannel = function(options) { - var remotePromise; - if (!options.negotiated) { - this.pcRemote.expectDataChannel(); - remotePromise = this.pcRemote.nextDataChannel; +PeerConnectionTest.prototype.createDataChannel = function DCT_createDataChannel(options, onSuccess) { + var localChannel = null; + var remoteChannel = null; + var self = this; + + // Method to synchronize all asynchronous events. + function check_next_test() { + if (localChannel && remoteChannel) { + onSuccess(localChannel, remoteChannel); + } } - // Create the datachannel - var localChannel = this.pcLocal.createDataChannel(options) - var localPromise = localChannel.opened; - - if (options.negotiated) { - remotePromise = localPromise.then(localChannel => { - // externally negotiated - we need to open from both ends - options.id = options.id || channel.id; // allow for no id on options - var remoteChannel = this.pcRemote.createDataChannel(options); - return remoteChannel.opened; + if (!options.negotiated) { + // Register handlers for the remote peer + this.pcRemote.registerDataChannelOpenEvents(function (channel) { + remoteChannel = channel; + check_next_test(); }); } - return Promise.all([localPromise, remotePromise]).then(result => { - return { local: result[0], remote: result[1] }; + // Create the datachannel and handle the local 'onopen' event + this.pcLocal.createDataChannel(options, function (channel) { + localChannel = channel; + + if (options.negotiated) { + // externally negotiated - we need to open from both ends + options.id = options.id || channel.id; // allow for no id to let the impl choose + self.pcRemote.createDataChannel(options, function (channel) { + remoteChannel = channel; + check_next_test(); + }); + } else { + check_next_test(); + } }); }; /** + * Executes the next command. + */ +PeerConnectionTest.prototype.next = function PCT_next() { + if (this._stepTimeout) { + clearTimeout(this._stepTimeout); + this._stepTimeout = null; + } + this.chain.executeNext(); +}; + +/** + * Set a timeout for the current step. + * @param {long] ms the number of milliseconds to allow for this step + */ +PeerConnectionTest.prototype.setStepTimeout = function(ms) { + this._stepTimeout = setTimeout(function() { + ok(false, "Step timed out: " + this.chain.currentStepLabel); + this.next(); + }.bind(this), ms); +}; + +/** + * Set a timeout for the over all PeerConnectionTest + * @param {long] ms the number of milliseconds to allow for the test + */ +PeerConnectionTest.prototype.setTimeout = function(ms) { + this._timeout = setTimeout(function() { + ok(false, "PeerConnectionTest timed out"); + this.teardown(); + }.bind(this), ms); +}; + +/** * Creates an answer for the specified peer connection instance * and automatically handles the failure case. * * @param {PeerConnectionWrapper} peer * The peer connection wrapper to run the command on + * @param {function} onSuccess + * Callback to execute if the offer was created successfully */ -PeerConnectionTest.prototype.createAnswer = function(peer) { - return peer.createAnswer().then(answer => { +PeerConnectionTest.prototype.createAnswer = +function PCT_createAnswer(peer, onSuccess) { + var self = this; + + peer.createAnswer(function (answer) { // make a copy so this does not get updated with ICE candidates - this.originalAnswer = new mozRTCSessionDescription(JSON.parse(JSON.stringify(answer))); - return answer; + self.originalAnswer = new mozRTCSessionDescription(JSON.parse(JSON.stringify(answer))); + onSuccess(answer); }); }; /** * Creates an offer for the specified peer connection instance * and automatically handles the failure case. * * @param {PeerConnectionWrapper} peer * The peer connection wrapper to run the command on + * @param {function} onSuccess + * Callback to execute if the offer was created successfully */ -PeerConnectionTest.prototype.createOffer = function(peer) { - return peer.createOffer().then(offer => { +PeerConnectionTest.prototype.createOffer = +function PCT_createOffer(peer, onSuccess) { + var self = this; + + peer.createOffer(function (offer) { // make a copy so this does not get updated with ICE candidates - this.originalOffer = new mozRTCSessionDescription(JSON.parse(JSON.stringify(offer))); - return offer; + self.originalOffer = new mozRTCSessionDescription(JSON.parse(JSON.stringify(offer))); + onSuccess(offer); }); }; PeerConnectionTest.prototype.setIdentityProvider = function(peer, provider, protocol, identity) { peer.setIdentityProvider(provider, protocol, identity); }; /** * Sets the local description for the specified peer connection instance * and automatically handles the failure case. * * @param {PeerConnectionWrapper} peer The peer connection wrapper to run the command on * @param {mozRTCSessionDescription} desc * Session description for the local description request + * @param {function} onSuccess + * Callback to execute if the local description was set successfully */ PeerConnectionTest.prototype.setLocalDescription = -function(peer, desc, stateExpected) { - var eventFired = new Promise(resolve => { - peer.onsignalingstatechange = e => { - info(peer + ": 'signalingstatechange' event received"); - var state = e.target.signalingState; - if (stateExpected === state) { - peer.setLocalDescStableEventDate = new Date(); - resolve(); - } else { - ok(false, "This event has either already fired or there has been a " + - "mismatch between event received " + state + - " and event expected " + stateExpected); - } - }; +function PCT_setLocalDescription(peer, desc, stateExpected, onSuccess) { + var eventFired = false; + var stateChanged = false; + + function check_next_test() { + if (eventFired && stateChanged) { + onSuccess(); + } + } + + peer.onsignalingstatechange = function (e) { + info(peer + ": 'signalingstatechange' event received"); + var state = e.target.signalingState; + if(stateExpected === state && !eventFired) { + eventFired = true; + peer.setLocalDescStableEventDate = new Date(); + check_next_test(); + } else { + ok(false, "This event has either already fired or there has been a " + + "mismatch between event received " + state + + " and event expected " + stateExpected); + } + }; + + peer.setLocalDescription(desc, function () { + stateChanged = true; + peer.setLocalDescDate = new Date(); + check_next_test(); }); - - var stateChanged = peer.setLocalDescription(desc).then(() => { - peer.setLocalDescDate = new Date(); - }); - - return Promise.all([eventFired, stateChanged]); }; /** * Sets the media constraints for both peer connection instances. * * @param {object} constraintsLocal * Media constrains for the local peer connection instance * @param constraintsRemote */ PeerConnectionTest.prototype.setMediaConstraints = -function(constraintsLocal, constraintsRemote) { - if (this.pcLocal) { +function PCT_setMediaConstraints(constraintsLocal, constraintsRemote) { + if (this.pcLocal) this.pcLocal.constraints = constraintsLocal; - } - if (this.pcRemote) { + if (this.pcRemote) this.pcRemote.constraints = constraintsRemote; - } }; /** * Sets the media options used on a createOffer call in the test. * * @param {object} options the media constraints to use on createOffer */ -PeerConnectionTest.prototype.setOfferOptions = function(options) { - if (this.pcLocal) { +PeerConnectionTest.prototype.setOfferOptions = +function PCT_setOfferOptions(options) { + if (this.pcLocal) this.pcLocal.offerOptions = options; - } }; /** * Sets the remote description for the specified peer connection instance * and automatically handles the failure case. * * @param {PeerConnectionWrapper} peer The peer connection wrapper to run the command on * @param {mozRTCSessionDescription} desc * Session description for the remote description request + * @param {function} onSuccess + * Callback to execute if the local description was set successfully */ PeerConnectionTest.prototype.setRemoteDescription = -function(peer, desc, stateExpected) { - var eventFired = new Promise(resolve => { - peer.onsignalingstatechange = e => { - info(peer + ": 'signalingstatechange' event received"); - var state = e.target.signalingState; - if (stateExpected === state) { - peer.setRemoteDescStableEventDate = new Date(); - resolve(); - } else { - ok(false, "This event has either already fired or there has been a " + - "mismatch between event received " + state + - " and event expected " + stateExpected); - } - }; +function PCT_setRemoteDescription(peer, desc, stateExpected, onSuccess) { + var eventFired = false; + var stateChanged = false; + + function check_next_test() { + if (eventFired && stateChanged) { + onSuccess(); + } + } + + peer.onsignalingstatechange = function(e) { + info(peer + ": 'signalingstatechange' event received"); + var state = e.target.signalingState; + if(stateExpected === state && !eventFired) { + eventFired = true; + peer.setRemoteDescStableEventDate = new Date(); + check_next_test(); + } else { + ok(false, "This event has either already fired or there has been a " + + "mismatch between event received " + state + + " and event expected " + stateExpected); + } + }; + + peer.setRemoteDescription(desc, function () { + stateChanged = true; + peer.setRemoteDescDate = new Date(); + check_next_test(); }); - - var stateChanged = peer.setRemoteDescription(desc).then(() => { - peer.setRemoteDescDate = new Date(); - }); - - return Promise.all([eventFired, stateChanged]); }; /** * Start running the tests as assigned to the command chain. */ -PeerConnectionTest.prototype.run = function() { - return this.chain.execute() - .then(() => this.close()) - .then(() => { - if (window.SimpleTest) { - networkTestFinished(); - } else { - finish(); - } - }) - .catch(e => - ok(false, 'Error in test execution: ' + e + - ((typeof e.stack === 'string') ? - (' ' + e.stack.split('\n').join(' ... ')) : ''))); +PeerConnectionTest.prototype.run = function PCT_run() { + this.next(); +}; + +/** + * Clean up the objects used by the test + */ +PeerConnectionTest.prototype.teardown = function PCT_teardown() { + this.close(function () { + info("Test finished"); + if (window.SimpleTest) + networkTestFinished(); + else + finish(); + }); }; /** * Routes ice candidates from one PCW to the other PCW */ -PeerConnectionTest.prototype.iceCandidateHandler = function(caller, candidate) { +PeerConnectionTest.prototype.iceCandidateHandler = function +PCT_iceCandidateHandler(caller, candidate) { + var self = this; + info("Received: " + JSON.stringify(candidate) + " from " + caller); var target = null; if (caller.contains("pcLocal")) { - if (this.pcRemote) { - target = this.pcRemote; + if (self.pcRemote) { + target = self.pcRemote; } } else if (caller.contains("pcRemote")) { - if (this.pcLocal) { - target = this.pcLocal; + if (self.pcLocal) { + target = self.pcLocal; } } else { ok(false, "received event from unknown caller: " + caller); return; } if (target) { target.storeOrAddIceCandidate(candidate); @@ -519,83 +1205,110 @@ PeerConnectionTest.prototype.iceCandidat send_message({"type": "ice_candidate", "ice_candidate": candidate}); } }; /** * Installs a polling function for the socket.io client to read * all messages from the chat room into a message queue. */ -PeerConnectionTest.prototype.setupSignalingClient = function() { - this.signalingMessageQueue = []; - this.signalingCallbacks = {}; - this.signalingLoopRun = true; +PeerConnectionTest.prototype.setupSignalingClient = function +PCT_setupSignalingClient() { + var self = this; - var queueMessage = message => { + self.signalingMessageQueue = []; + self.signalingCallbacks = {}; + self.signalingLoopRun = true; + + function queueMessage(message) { info("Received signaling message: " + JSON.stringify(message)); var fired = false; - Object.keys(this.signalingCallbacks).forEach(name => { + Object.keys(self.signalingCallbacks).forEach(function(name) { if (name === message.type) { info("Invoking callback for message type: " + name); - this.signalingCallbacks[name](message); + self.signalingCallbacks[name](message); fired = true; } }); if (!fired) { - this.signalingMessageQueue.push(message); - info("signalingMessageQueue.length: " + this.signalingMessageQueue.length); + self.signalingMessageQueue.push(message); + info("signalingMessageQueue.length: " + self.signalingMessageQueue.length); } - if (this.signalingLoopRun) { + if (self.signalingLoopRun) { wait_for_message().then(queueMessage); } else { info("Exiting signaling message event loop"); } - }; + } + wait_for_message().then(queueMessage); } /** * Sets a flag to stop reading further messages from the chat room. */ -PeerConnectionTest.prototype.signalingMessagesFinished = function() { +PeerConnectionTest.prototype.signalingMessagesFinished = function +PCT_signalingMessagesFinished() { this.signalingLoopRun = false; } /** + * Callback to stop reading message from chat room once trickle ICE + * on the far end is over. + * + * @param {string} caller + * The lable of the caller of the function + */ +PeerConnectionTest.prototype.signalEndOfTrickleIce = function +PCT_signalEndOfTrickleIce(caller) { + if (this.steeplechase) { + send_message({"type": "end_of_trickle_ice"}); + } +}; + +/** * Register a callback function to deliver messages from the chat room * directly instead of storing them in the message queue. * * @param {string} messageType * For which message types should the callback get invoked. * * @param {function} onMessage * The function which gets invoked if a message of the messageType * has been received from the chat room. */ -PeerConnectionTest.prototype.registerSignalingCallback = function(messageType, onMessage) { +PeerConnectionTest.prototype.registerSignalingCallback = function +PCT_registerSignalingCallback(messageType, onMessage) { this.signalingCallbacks[messageType] = onMessage; -}; +} /** * Searches the message queue for the first message of a given type * and invokes the given callback function, or registers the callback * function for future messages if the queue contains no such message. * * @param {string} messageType * The type of message to search and register for. + * + * @param {function} onMessage + * The callback function which gets invoked with the messages + * of the given mesage type. */ -PeerConnectionTest.prototype.getSignalingMessage = function(messageType) { - var i = this.signalingMessageQueue.findIndex(m => m.type === messageType); - if (i >= 0) { - info("invoking callback on message " + i + " from message queue, for message type:" + messageType); - return Promise.resolve(this.signalingMessageQueue.splice(i, 1)[0]); +PeerConnectionTest.prototype.getSignalingMessage = function +PCT_getSignalingMessage(messageType, onMessage) { + for(var i=0; i < this.signalingMessageQueue.length; i++) { + if (messageType === this.signalingMessageQueue[i].type) { + //FIXME + info("invoking callback on message " + i + " from message queue, for message type:" + messageType); + onMessage(this.signalingMessageQueue.splice(i, 1)[0]); + return; + } } - return new Promise(resolve => - this.registerSignalingCallback(messageType, resolve)); -}; + this.registerSignalingCallback(messageType, onMessage); +} /** * This class acts as a wrapper around a DataChannel instance. * * @param dataChannel * @param peerConnectionWrapper * @constructor @@ -604,27 +1317,62 @@ function DataChannelWrapper(dataChannel, this._channel = dataChannel; this._pc = peerConnectionWrapper; info("Creating " + this); /** * Setup appropriate callbacks */ - createOneShotEventWrapper(this, this._channel, 'close'); - createOneShotEventWrapper(this, this._channel, 'error'); - createOneShotEventWrapper(this, this._channel, 'message'); + + this.onclose = unexpectedEventAndFinish(this, 'onclose'); + this.onerror = unexpectedEventAndFinish(this, 'onerror'); + this.onmessage = unexpectedEventAndFinish(this, 'onmessage'); + this.onopen = unexpectedEventAndFinish(this, 'onopen'); + + var self = this; + + /** + * Callback for native data channel 'onclose' events. If no custom handler + * has been specified via 'this.onclose', a failure will be raised if an + * event of this type gets caught. + */ + this._channel.onclose = function () { + info(self + ": 'onclose' event fired"); + + self.onclose(self); + self.onclose = unexpectedEventAndFinish(self, 'onclose'); + }; - this.opened = timerGuard(new Promise(resolve => { - this._channel.onopen = () => { - this._channel.onopen = unexpectedEvent(this, 'onopen'); - is(this.readyState, "open", "data channel is 'open' after 'onopen'"); - resolve(this); - }; - }), 60000, "channel didn't open in time"); + /** + * Callback for native data channel 'onmessage' events. If no custom handler + * has been specified via 'this.onmessage', a failure will be raised if an + * event of this type gets caught. + * + * @param {Object} event + * Event data which includes the sent message + */ + this._channel.onmessage = function (event) { + info(self + ": 'onmessage' event fired for '" + event.data + "'"); + + self.onmessage(event.data); + self.onmessage = unexpectedEventAndFinish(self, 'onmessage'); + }; + + /** + * Callback for native data channel 'onopen' events. If no custom handler + * has been specified via 'this.onopen', a failure will be raised if an + * event of this type gets caught. + */ + this._channel.onopen = function () { + info(self + ": 'onopen' event fired"); + + self.onopen(self); + self.onopen = unexpectedEventAndFinish(self, 'onopen'); + }; } DataChannelWrapper.prototype = { /** * Returns the binary type of the channel * * @returns {String} The binary type */ @@ -698,27 +1446,27 @@ DataChannelWrapper.prototype = { }, /** * Send data through the data channel * * @param {String|Object} data * Data which has to be sent through the data channel */ - send: function(data) { + send: function DCW_send(data) { info(this + ": Sending data '" + data + "'"); this._channel.send(data); }, /** * Returns the string representation of the class * * @returns {String} The string representation */ - toString: function() { + toString: function DCW_toString() { return "DataChannelWrapper (" + this._pc.label + '_' + this._channel.label + ")"; } }; /** * This class acts as a wrapper around a PeerConnection instance. * @@ -735,73 +1483,119 @@ function PeerConnectionWrapper(label, co this.constraints = [ ]; this.offerOptions = {}; this.streams = [ ]; this.mediaCheckers = [ ]; this.dataChannels = [ ]; - this.addStreamCounter = {audio: 0, video: 0 }; + this.onAddStreamAudioCounter = 0; + this.onAddStreamVideoCounter = 0; + this.addStreamCallbacks = {}; this._local_ice_candidates = []; this._remote_ice_candidates = []; - this.holdIceCandidates = new Promise(r => this.releaseIceCandidates = r); + this._ice_candidates_to_add = []; + this.holdIceCandidates = true; + this.endOfTrickleIce = false; this.localRequiresTrickleIce = false; - this.remoteRequiresTrickleIce = false; - this.localMediaElements = []; + this.remoteRequiresTrickleIce = false; this.h264 = typeof h264 !== "undefined" ? true : false; info("Creating " + this); this._pc = new mozRTCPeerConnection(this.configuration); /** * Setup callback handlers */ + var self = this; + // This enables tests to validate that the next ice state is the one they expect to happen + this.next_ice_state = ""; // in most cases, the next state will be "checking", but in some tests "closed" // This allows test to register their own callbacks for ICE connection state changes this.ice_connection_callbacks = {}; - this._pc.oniceconnectionstatechange = e => { - isnot(typeof this._pc.iceConnectionState, "undefined", - "iceConnectionState should not be undefined"); - info(this + ": oniceconnectionstatechange fired, new state is: " + this._pc.iceConnectionState); - Object.keys(this.ice_connection_callbacks).forEach(name => { - this.ice_connection_callbacks[name](); + this._pc.oniceconnectionstatechange = function() { + ok(self._pc.iceConnectionState !== undefined, "iceConnectionState should not be undefined"); + info(self + ": oniceconnectionstatechange fired, new state is: " + self._pc.iceConnectionState); + Object.keys(self.ice_connection_callbacks).forEach(function(name) { + self.ice_connection_callbacks[name](); }); + if (self.next_ice_state !== "") { + is(self._pc.iceConnectionState, self.next_ice_state, "iceConnectionState changed to '" + + self.next_ice_state + "'"); + self.next_ice_state = ""; + } }; /** * Callback for native peer connection 'onaddstream' events. * * @param {Object} event * Event data which includes the stream to be added */ - this._pc.onaddstream = event => { - info(this + ": 'onaddstream' event fired for " + JSON.stringify(event.stream)); + this._pc.onaddstream = function (event) { + info(self + ": 'onaddstream' event fired for " + JSON.stringify(event.stream)); var type = ''; if (event.stream.getAudioTracks().length > 0) { type = 'audio'; - this.addStreamCounter.audio += this.countTracksInStreams('audio', [event.stream]); + self.onAddStreamAudioCounter += event.stream.getAudioTracks().length; } if (event.stream.getVideoTracks().length > 0) { type += 'video'; - this.addStreamCounter.video += this.countTracksInStreams('video', [event.stream]); + self.onAddStreamVideoCounter += event.stream.getVideoTracks().length; } - this.attachMedia(event.stream, type, 'remote'); + self.attachMedia(event.stream, type, 'remote'); + + Object.keys(self.addStreamCallbacks).forEach(function(name) { + info(self + " calling addStreamCallback " + name); + self.addStreamCallbacks[name](); + }); + }; + + this.ondatachannel = unexpectedEventAndFinish(this, 'ondatachannel'); + + /** + * Callback for native peer connection 'ondatachannel' events. If no custom handler + * has been specified via 'this.ondatachannel', a failure will be raised if an + * event of this type gets caught. + * + * @param {Object} event + * Event data which includes the newly created data channel + */ + this._pc.ondatachannel = function (event) { + info(self + ": 'ondatachannel' event fired for " + event.channel.label); + + self.ondatachannel(new DataChannelWrapper(event.channel, self)); + self.ondatachannel = unexpectedEventAndFinish(self, 'ondatachannel'); }; - createOneShotEventWrapper(this, this._pc, 'datachannel'); - this._pc.addEventListener('datachannel', e => { - var wrapper = new DataChannelWrapper(e.channel, this); - this.dataChannels.push(wrapper); - }); + this.onsignalingstatechange = unexpectedEventAndFinish(this, 'onsignalingstatechange'); + this.signalingStateCallbacks = {}; - createOneShotEventWrapper(this, this._pc, 'signalingstatechange'); + /** + * Callback for native peer connection 'onsignalingstatechange' events. If no + * custom handler has been specified via 'this.onsignalingstatechange', a + * failure will be raised if an event of this type is caught. + * + * @param {Object} aEvent + */ + this._pc.onsignalingstatechange = function (anEvent) { + info(self + ": 'onsignalingstatechange' event fired"); + + Object.keys(self.signalingStateCallbacks).forEach(function(name) { + self.signalingStateCallbacks[name](anEvent); + }); + // this calls the eventhandler only once and then overwrites it with the + // default unexpectedEvent handler + self.onsignalingstatechange(anEvent); + self.onsignalingstatechange = unexpectedEventAndFinish(self, 'onsignalingstatechange'); + }; } PeerConnectionWrapper.prototype = { /** * Returns the local description. * * @returns {object} The local description @@ -866,380 +1660,489 @@ PeerConnectionWrapper.prototype = { * * @param {MediaStream} stream * Media stream to handle * @param {string} type * The type of media stream ('audio' or 'video') * @param {string} side * The location the stream is coming from ('local' or 'remote') */ - attachMedia : function(stream, type, side) { + attachMedia : function PCW_attachMedia(stream, type, side) { + function isSenderOfTrack(sender) { + return sender.track == this; + } + info("Got media stream: " + type + " (" + side + ")"); this.streams.push(stream); if (side === 'local') { // In order to test both the addStream and addTrack APIs, we do video one // way and audio + audiovideo the other. if (type == "video") { this._pc.addStream(stream); - ok(this._pc.getSenders().find(sender => sender.track == stream.getVideoTracks()[0]), + ok(this._pc.getSenders().find(isSenderOfTrack, + stream.getVideoTracks()[0]), "addStream adds sender"); } else { - stream.getTracks().forEach(track => { + stream.getTracks().forEach(function(track) { var sender = this._pc.addTrack(track, stream); is(sender.track, track, "addTrack returns sender"); - }); + }.bind(this)); } } - var element = createMediaElement(type, this.label + '_' + side + this.streams.length); + var element = createMediaElement(type, this.label + '_' + side); this.mediaCheckers.push(new MediaElementChecker(element)); element.mozSrcObject = stream; element.play(); - - // Store local media elements so that we can stop them when done. - // Don't store remote ones because they should stop when the PC does. - if (side === 'local') { - this.localMediaElements.push(element); - } }, /** * Requests all the media streams as specified in the constrains property. * + * @param {function} onSuccess + * Callback to execute if all media has been requested successfully * @param {array} constraintsList * Array of constraints for GUM calls */ - getAllUserMedia : function(constraintsList) { - if (constraintsList.length === 0) { - info("Skipping GUM: no UserMedia requested"); - return Promise.resolve(); + getAllUserMedia : function PCW_GetAllUserMedia(constraintsList, onSuccess) { + var self = this; + + function _getAllUserMedia(index) { + if (index < constraintsList.length) { + var constraints = constraintsList[index]; + + getUserMedia(constraints, function (stream) { + var type = ''; + + if (constraints.audio) { + type = 'audio'; + } + + if (constraints.video) { + type += 'video'; + } + + self.attachMedia(stream, type, 'local'); + + _getAllUserMedia(index + 1); + }, generateErrorCallback()); + } else { + onSuccess(); + } } - info("Get " + constraintsList.length + " local streams"); - return Promise.all(constraintsList.map(constraints => { - return getUserMedia(constraints).then(stream => { - var type = ''; - if (constraints.audio) { - type = 'audio'; - } - if (constraints.video) { - type += 'video'; - } - this.attachMedia(stream, type, 'local'); - }); - })); - }, - - /** - * Create a new data channel instance. Also creates a promise called - * `this.nextDataChannel` that resolves when the next data channel arrives. - */ - expectDataChannel: function(message) { - this.nextDataChannel = new Promise(resolve => { - this.ondatachannel = e => { - ok(e.channel, message); - resolve(e.channel); - }; - }); + if (constraintsList.length === 0) { + info("Skipping GUM: no UserMedia requested"); + onSuccess(); + } + else { + info("Get " + constraintsList.length + " local streams"); + _getAllUserMedia(0); + } }, /** * Create a new data channel instance * * @param {Object} options * Options which get forwarded to nsIPeerConnection.createDataChannel + * @param {function} [onCreation=undefined] + * Callback to execute when the local data channel has been created * @returns {DataChannelWrapper} The created data channel */ - createDataChannel : function(options) { + createDataChannel : function PCW_createDataChannel(options, onCreation) { var label = 'channel_' + this.dataChannels.length; info(this + ": Create data channel '" + label); var channel = this._pc.createDataChannel(label, options); var wrapper = new DataChannelWrapper(channel, this); + + if (onCreation) { + wrapper.onopen = function () { + onCreation(wrapper); + }; + } + this.dataChannels.push(wrapper); return wrapper; }, /** * Creates an offer and automatically handles the failure case. * * @param {function} onSuccess * Callback to execute if the offer was created successfully */ - createOffer : function() { - return this._pc.createOffer(this.offerOptions).then(offer => { + createOffer : function PCW_createOffer(onSuccess) { + var self = this; + + this._pc.createOffer(function (offer) { info("Got offer: " + JSON.stringify(offer)); // note: this might get updated through ICE gathering - this._latest_offer = offer; - if (this.h264) { + self._latest_offer = offer; + if (self.h264) { isnot(offer.sdp.search("H264/90000"), -1, "H.264 should be present in the SDP offer"); offer.sdp = removeVP8(offer.sdp); } - return offer; - }); + onSuccess(offer); + }, generateErrorCallback(), this.offerOptions); }, /** * Creates an answer and automatically handles the failure case. + * + * @param {function} onSuccess + * Callback to execute if the answer was created successfully */ - createAnswer : function() { - return this._pc.createAnswer().then(answer => { - info(this + ": Got answer: " + JSON.stringify(answer)); - this._last_answer = answer; - return answer; - }); + createAnswer : function PCW_createAnswer(onSuccess) { + var self = this; + + this._pc.createAnswer(function (answer) { + info(self + ": Got answer: " + JSON.stringify(answer)); + self._last_answer = answer; + onSuccess(answer); + }, generateErrorCallback()); }, /** * Sets the local description and automatically handles the failure case. * * @param {object} desc * mozRTCSessionDescription for the local description request + * @param {function} onSuccess + * Callback to execute if the local description was set successfully */ - setLocalDescription : function(desc) { - return this._pc.setLocalDescription(desc).then(() => { - info(this + ": Successfully set the local description"); - }); + setLocalDescription : function PCW_setLocalDescription(desc, onSuccess) { + var self = this; + + if (onSuccess) { + this._pc.setLocalDescription(desc, function () { + info(self + ": Successfully set the local description"); + onSuccess(); + }, generateErrorCallback()); + } else { + this._pc.setLocalDescription(desc); + } }, /** * Tries to set the local description and expect failure. Automatically * causes the test case to fail if the call succeeds. * * @param {object} desc * mozRTCSessionDescription for the local description request - * @returns {Promise} - * A promise that resolves to the expected error + * @param {function} onFailure + * Callback to execute if the call fails. */ - setLocalDescriptionAndFail : function(desc) { - return this._pc.setLocalDescription(desc).then( + setLocalDescriptionAndFail : function PCW_setLocalDescriptionAndFail(desc, onFailure) { + var self = this; + this._pc.setLocalDescription(desc, generateErrorCallback("setLocalDescription should have failed."), - err => { - info(this + ": As expected, failed to set the local description"); - return err; - }); + function (err) { + info(self + ": As expected, failed to set the local description"); + onFailure(err); + }); }, /** * Sets the remote description and automatically handles the failure case. * * @param {object} desc * mozRTCSessionDescription for the remote description request + * @param {function} onSuccess + * Callback to execute if the remote description was set successfully */ - setRemoteDescription : function(desc) { - return this._pc.setRemoteDescription(desc).then(() => { - info(this + ": Successfully set remote description"); - this.releaseIceCandidates(); - }); + setRemoteDescription : function PCW_setRemoteDescription(desc, onSuccess) { + var self = this; + + if (!onSuccess) { + this._pc.setRemoteDescription(desc); + this.addStoredIceCandidates(); + return; + } + this._pc.setRemoteDescription(desc, function () { + info(self + ": Successfully set remote description"); + self.addStoredIceCandidates(); + onSuccess(); + }, generateErrorCallback()); }, /** * Tries to set the remote description and expect failure. Automatically * causes the test case to fail if the call succeeds. * * @param {object} desc * mozRTCSessionDescription for the remote description request - * @returns {Promise} - * a promise that resolve to the returned error + * @param {function} onFailure + * Callback to execute if the call fails. */ - setRemoteDescriptionAndFail : function(desc) { - return this._pc.setRemoteDescription(desc).then( + setRemoteDescriptionAndFail : function PCW_setRemoteDescriptionAndFail(desc, onFailure) { + var self = this; + this._pc.setRemoteDescription(desc, generateErrorCallback("setRemoteDescription should have failed."), - err => { - info(this + ": As expected, failed to set the remote description"); - return err; + function (err) { + info(self + ": As expected, failed to set the remote description"); + onFailure(err); }); }, /** * Registers a callback for the signaling state change and * appends the new state to an array for logging it later. */ - logSignalingState: function() { - this.signalingStateLog = [this._pc.signalingState]; - this._pc.addEventListener('signalingstatechange', e => { - var newstate = this._pc.signalingState; - var oldstate = this.signalingStateLog[this.signalingStateLog.length - 1] - if (Object.keys(signalingStateTransitions).indexOf(oldstate) >= 0) { - ok(signalingStateTransitions[oldstate].indexOf(newstate) >= 0, this + ": legal signaling state transition from " + oldstate + " to " + newstate); + logSignalingState: function PCW_logSignalingState() { + var self = this; + + function _logSignalingState(e) { + var newstate = self._pc.signalingState; + var oldstate = self.signalingStateLog[self.signalingStateLog.length - 1] + if (Object.keys(signalingStateTransitions).indexOf(oldstate) != -1) { + ok(signalingStateTransitions[oldstate].indexOf(newstate) != -1, self + ": legal signaling state transition from " + oldstate + " to " + newstate); } else { - ok(false, this + ": old signaling state " + oldstate + " missing in signaling transition array"); + ok(false, self + ": old signaling state " + oldstate + " missing in signaling transition array"); } - this.signalingStateLog.push(newstate); - }); + self.signalingStateLog.push(newstate); + } + + self.signalingStateLog = [self._pc.signalingState]; + self.signalingStateCallbacks.logSignalingStatus = _logSignalingState; }, /** * Either adds a given ICE candidate right away or stores it to be added * later, depending on the state of the PeerConnection. * * @param {object} candidate * The mozRTCIceCandidate to be added or stored */ - storeOrAddIceCandidate : function(candidate) { - this._remote_ice_candidates.push(candidate); - if (this.signalingState === 'closed') { + storeOrAddIceCandidate : function PCW_storeOrAddIceCandidate(candidate) { + var self = this; + + self._remote_ice_candidates.push(candidate); + if (self.signalingState === 'closed') { info("Received ICE candidate for closed PeerConnection - discarding"); return; } - this.holdIceCandidates.then(() => { - this.addIceCandidate(candidate); - }); + if (!self.holdIceCandidates) { + self.addIceCandidate(candidate); + } else { + self._ice_candidates_to_add.push(candidate); + } + }, + + addStoredIceCandidates : function PCW_addStoredIceCandidates() { + var self = this; + + self.holdIceCandidates = false; + if ((self._ice_candidates_to_add) && + (self._ice_candidates_to_add.length > 0)) { + info("adding stored ice candidates"); + for (var i = 0; i < self._ice_candidates_to_add.length; i++) { + self.addIceCandidate(self._ice_candidates_to_add[i]); + } + self._ice_candidates_to_add = []; + } }, /** * Adds an ICE candidate and automatically handles the failure case. * * @param {object} candidate * SDP candidate + * @param {function} onSuccess + * Callback to execute if the local description was set successfully */ - addIceCandidate : function(candidate) { - info(this + ": adding ICE candidate " + JSON.stringify(candidate)); - return this._pc.addIceCandidate(candidate).then(() => { - info(this + ": Successfully added an ICE candidate"); - }); + addIceCandidate : function PCW_addIceCandidate(candidate, onSuccess) { + var self = this; + + info(self + ": adding ICE candidate " + JSON.stringify(candidate)); + this._pc.addIceCandidate(candidate, function () { + info(self + ": Successfully added an ICE candidate"); + if (onSuccess) { + onSuccess(); + } + }, generateErrorCallback()); + }, + + /** + * Tries to add an ICE candidate and expects failure. Automatically + * causes the test case to fail if the call succeeds. + * + * @param {object} candidate + * SDP candidate + * @param {function} onFailure + * Callback to execute if the call fails. + */ + addIceCandidateAndFail : function PCW_addIceCandidateAndFail(candidate, onFailure) { + var self = this; + + this._pc.addIceCandidate(candidate, + generateErrorCallback("addIceCandidate should have failed."), + function (err) { + info(self + ": As expected, failed to add an ICE candidate"); + onFailure(err); + }) ; }, /** * Returns if the ICE the connection state is "connected". * * @returns {boolean} True if the connection state is "connected", otherwise false. */ - isIceConnected : function() { + isIceConnected : function PCW_isIceConnected() { info(this + ": iceConnectionState = " + this.iceConnectionState); return this.iceConnectionState === "connected"; }, /** * Returns if the ICE the connection state is "checking". * * @returns {boolean} True if the connection state is "checking", otherwise false. */ - isIceChecking : function() { + isIceChecking : function PCW_isIceChecking() { return this.iceConnectionState === "checking"; }, /** * Returns if the ICE the connection state is "new". * * @returns {boolean} True if the connection state is "new", otherwise false. */ - isIceNew : function() { + isIceNew : function PCW_isIceNew() { return this.iceConnectionState === "new"; }, /** * Checks if the ICE connection state still waits for a connection to get * established. * * @returns {boolean} True if the connection state is "checking" or "new", * otherwise false. */ - isIceConnectionPending : function() { + isIceConnectionPending : function PCW_isIceConnectionPending() { return (this.isIceChecking() || this.isIceNew()); }, /** * Registers a callback for the ICE connection state change and * appends the new state to an array for logging it later. */ - logIceConnectionState: function() { - this.iceConnectionLog = [this._pc.iceConnectionState]; - this.ice_connection_callbacks.logIceStatus = () => { - var newstate = this._pc.iceConnectionState; - var oldstate = this.iceConnectionLog[this.iceConnectionLog.length - 1] + logIceConnectionState: function PCW_logIceConnectionState() { + var self = this; + + function logIceConState () { + var newstate = self._pc.iceConnectionState; + var oldstate = self.iceConnectionLog[self.iceConnectionLog.length - 1] if (Object.keys(iceStateTransitions).indexOf(oldstate) != -1) { - ok(iceStateTransitions[oldstate].indexOf(newstate) != -1, this + ": legal ICE state transition from " + oldstate + " to " + newstate); + ok(iceStateTransitions[oldstate].indexOf(newstate) != -1, self + ": legal ICE state transition from " + oldstate + " to " + newstate); } else { - ok(false, this + ": old ICE state " + oldstate + " missing in ICE transition array"); + ok(false, self + ": old ICE state " + oldstate + " missing in ICE transition array"); } - this.iceConnectionLog.push(newstate); - }; + self.iceConnectionLog.push(newstate); + } + + self.iceConnectionLog = [self._pc.iceConnectionState]; + self.ice_connection_callbacks.logIceStatus = logIceConState; }, /** * Registers a callback for the ICE connection state change and * reports success (=connected) or failure via the callbacks. * States "new" and "checking" are ignored. * - * @returns {Promise} - * resolves when connected, rejects on failure + * @param {function} onSuccess + * Callback if ICE connection status is "connected". + * @param {function} onFailure + * Callback if ICE connection reaches a different state than + * "new", "checking" or "connected". */ - waitForIceConnected : function() { - return new Promise((resolve, reject) => { - var iceConnectedChanged = () => { - if (this.isIceConnected()) { - delete this.ice_connection_callbacks.waitForIceConnected; - resolve(); - } else if (! this.isIceConnectionPending()) { - delete this.ice_connection_callbacks.waitForIceConnected; - resolve(); - } + waitForIceConnected : function PCW_waitForIceConnected(onSuccess, onFailure) { + var self = this; + var mySuccess = onSuccess; + var myFailure = onFailure; + + function iceConnectedChanged () { + if (self.isIceConnected()) { + delete self.ice_connection_callbacks.waitForIceConnected; + mySuccess(); + } else if (! self.isIceConnectionPending()) { + delete self.ice_connection_callbacks.waitForIceConnected; + myFailure(); } + } - this.ice_connection_callbacks.waitForIceConnected = iceConnectedChanged; - }); + self.ice_connection_callbacks.waitForIceConnected = iceConnectedChanged; }, /** * Setup a onicecandidate handler * * @param {object} test * A PeerConnectionTest object to which the ice candidates gets * forwarded. */ - setupIceCandidateHandler : function(test, candidateHandler) { - candidateHandler = candidateHandler || test.iceCandidateHandler.bind(test); - - var resolveEndOfTrickle; - this.endOfTrickleIce = new Promise(r => resolveEndOfTrickle = r); + setupIceCandidateHandler : function + PCW_setupIceCandidateHandler(test, candidateHandler, endHandler) { + var self = this; - this.endOfTrickleIce.then(() => { - this._pc.onicecandidate = () => - ok(false, this.label + " received ICE candidate after end of trickle"); - }); + candidateHandler = candidateHandler || test.iceCandidateHandler.bind(test); + endHandler = endHandler || test.signalEndOfTrickleIce.bind(test); - this._pc.onicecandidate = anEvent => { + function iceCandidateCallback (anEvent) { + info(self.label + ": received iceCandidateEvent"); if (!anEvent.candidate) { - info(this.label + ": received end of trickle ICE event"); - resolveEndOfTrickle(this.label); - return; + info(self.label + ": received end of trickle ICE event"); + self.endOfTrickleIce = true; + endHandler(self.label); + } else { + if (self.endOfTrickleIce) { + ok(false, "received ICE candidate after end of trickle"); + } + info(self.label + ": iceCandidate = " + JSON.stringify(anEvent.candidate)); + ok(anEvent.candidate.candidate.length > 0, "ICE candidate contains candidate"); + // we don't support SDP MID's yet + ok(anEvent.candidate.sdpMid.length === 0, "SDP MID has length zero"); + ok(typeof anEvent.candidate.sdpMLineIndex === 'number', "SDP MLine Index needs to exist"); + self._local_ice_candidates.push(anEvent.candidate); + candidateHandler(self.label, anEvent.candidate); } + } - info(this.label + ": iceCandidate = " + JSON.stringify(anEvent.candidate)); - ok(anEvent.candidate.candidate.length > 0, "ICE candidate contains candidate"); - // we don't support SDP MID's yet - ok(anEvent.candidate.sdpMid.length === 0, "SDP MID has length zero"); - ok(typeof anEvent.candidate.sdpMLineIndex === 'number', "SDP MLine Index needs to exist"); - this._local_ice_candidates.push(anEvent.candidate); - candidateHandler(this.label, anEvent.candidate); - }; + self._pc.onicecandidate = iceCandidateCallback; }, /** * Counts the amount of audio tracks in a given media constraint. * * @param constraints * The contraint to be examined. */ - countTracksInConstraint : function(type, constraints) { - if (!Array.isArray(constraints)) { + countAudioTracksInMediaConstraint : function + PCW_countAudioTracksInMediaConstraint(constraints) { + if ((!constraints) || (constraints.length === 0)) { return 0; } - return constraints.reduce((sum, c) => sum + (c[type] ? 1 : 0), 0); + var numAudioTracks = 0; + for (var i = 0; i < constraints.length; i++) { + if (constraints[i].audio) { + numAudioTracks++; + } + } + return numAudioTracks; }, /** * Checks for audio in given offer options. * * @param options * The options to be examined. */ - audioInOfferOptions : function(options) { + audioInOfferOptions : function + PCW_audioInOfferOptions(options) { if (!options) { return 0; } var offerToReceiveAudio = options.offerToReceiveAudio; // TODO: Remove tests of old constraint-like RTCOptions soon (Bug 1064223). if (options.mandatory && options.mandatory.OfferToReceiveAudio !== undefined) { @@ -1252,22 +2155,43 @@ PeerConnectionWrapper.prototype = { if (offerToReceiveAudio) { return 1; } else { return 0; } }, /** + * Counts the amount of video tracks in a given media constraint. + * + * @param constraint + * The contraint to be examined. + */ + countVideoTracksInMediaConstraint : function + PCW_countVideoTracksInMediaConstraint(constraints) { + if ((!constraints) || (constraints.length === 0)) { + return 0; + } + var numVideoTracks = 0; + for (var i = 0; i < constraints.length; i++) { + if (constraints[i].video) { + numVideoTracks++; + } + } + return numVideoTracks; + }, + + /** * Checks for video in given offer options. * * @param options * The options to be examined. */ - videoInOfferOptions : function(options) { + videoInOfferOptions : function + PCW_videoInOfferOptions(options) { if (!options) { return 0; } var offerToReceiveVideo = options.offerToReceiveVideo; // TODO: Remove tests of old constraint-like RTCOptions soon (Bug 1064223). if (options.mandatory && options.mandatory.OfferToReceiveVideo !== undefined) { @@ -1280,140 +2204,168 @@ PeerConnectionWrapper.prototype = { if (offerToReceiveVideo) { return 1; } else { return 0; } }, /* - * Counts the amount of tracks of the given type in a set of streams. + * Counts the amount of audio tracks in a given set of streams. * - * @param type audio|video * @param streams * An array of streams (as returned by getLocalStreams()) to be * examined. */ - countTracksInStreams: function(type, streams) { - if (!Array.isArray(streams)) { + countAudioTracksInStreams : function PCW_countAudioTracksInStreams(streams) { + if (!streams || (streams.length === 0)) { return 0; } - var f = (type === 'video') ? "getVideoTracks" : "getAudioTracks"; + var numAudioTracks = 0; + streams.forEach(function(st) { + numAudioTracks += st.getAudioTracks().length; + }); + return numAudioTracks; + }, - return streams.reduce((count, st) => { - return count + st[f]().length; - }, 0); + /* + * Counts the amount of video tracks in a given set of streams. + * + * @param streams + * An array of streams (as returned by getLocalStreams()) to be + * examined. + */ + countVideoTracksInStreams: function PCW_countVideoTracksInStreams(streams) { + if (!streams || (streams.length === 0)) { + return 0; + } + var numVideoTracks = 0; + streams.forEach(function(st) { + numVideoTracks += st.getVideoTracks().length; + }); + return numVideoTracks; }, /** * Checks that we are getting the media tracks we expect. * - * @param {object} constraints - * The media constraints of the remote peer connection object + * @param {object} constraintsRemote + * The media constraints of the local and remote peer connection object */ - checkMediaTracks : function(remoteConstraints) { - var waitForExpectedTracks = type => { - var outstandingCount = this.countTracksInConstraint(type, remoteConstraints); - outstandingCount -= this.addStreamCounter[type]; - if (outstandingCount <= 0) { - return Promise.resolve(); - } + checkMediaTracks : function PCW_checkMediaTracks(constraintsRemote, onSuccess) { + var self = this; + + function _checkMediaTracks(constraintsRemote, onSuccess) { + + var localConstraintAudioTracks = + self.countAudioTracksInMediaConstraint(self.constraints); + var localStreams = self._pc.getLocalStreams(); + var localAudioTracks = self.countAudioTracksInStreams(localStreams, false); + is(localAudioTracks, localConstraintAudioTracks, self + ' has ' + + localAudioTracks + ' local audio tracks'); + + var localConstraintVideoTracks = + self.countVideoTracksInMediaConstraint(self.constraints); + var localVideoTracks = self.countVideoTracksInStreams(localStreams, false); + is(localVideoTracks, localConstraintVideoTracks, self + ' has ' + + localVideoTracks + ' local video tracks'); + + var remoteConstraintAudioTracks = + self.countAudioTracksInMediaConstraint(constraintsRemote); + var remoteStreams = self._pc.getRemoteStreams(); + var remoteAudioTracks = self.countAudioTracksInStreams(remoteStreams, false); + is(remoteAudioTracks, remoteConstraintAudioTracks, self + ' has ' + + remoteAudioTracks + ' remote audio tracks'); + + var remoteConstraintVideoTracks = + self.countVideoTracksInMediaConstraint(constraintsRemote); + var remoteVideoTracks = self.countVideoTracksInStreams(remoteStreams, false); + is(remoteVideoTracks, remoteConstraintVideoTracks, self + ' has ' + + remoteVideoTracks + ' remote video tracks'); - return new Promise(resolve => { - this._pc.addEventListener('addstream', e => { - outstandingCount -= this.countTracksInStreams(type, [e.stream]); - if (outstandingCount <= 0) { - resolve(); - } - }); - }); - }; + onSuccess(); + } + + // we have to do this check as the onaddstream never fires if the remote + // stream has no track at all! + var expectedRemoteTracks = + self.countAudioTracksInMediaConstraint(constraintsRemote) + + self.countVideoTracksInMediaConstraint(constraintsRemote); + + // TODO: this whole counting of streams should be replaced with comparing + // media stream objects IDs and what we got in the SDP (bug 1089798) + function _compareReceivedAndExpectedTracks(constraintsRemote, onSuccess) { + var receivedRemoteTracks = + self.onAddStreamAudioCounter + self.onAddStreamVideoCounter; - var checkTrackCounts = (side, streams, constraints) => { - ['audio', 'video'].forEach(type => { - var actual = this.countTracksInStreams(type, streams); - var expected = this.countTracksInConstraint(type, constraints); - is(actual, expected, this + ' has ' + actual + ' ' + - side + ' ' + type + ' tracks'); - }); - }; + if (receivedRemoteTracks === expectedRemoteTracks) { + _checkMediaTracks(constraintsRemote, onSuccess); + } else if (receivedRemoteTracks > expectedRemoteTracks) { + ok(false, "Received more streams " + receivedRemoteTracks + + " then expected " + expectedRemoteTracks); + _checkMediaTracks(constraintsRemote, onSuccess); + } else { + info("Still waiting for more remote streams to arrive (" + + receivedRemoteTracks + " vs " + expectedRemoteTracks + ")"); + } + } - info(this + " checkMediaTracks() got called before onAddStream fired"); - var checkPromise = Promise.all([ - waitForExpectedTracks('audio'), - waitForExpectedTracks('video') - ]).then(() => { - checkTrackCounts('local', this._pc.getLocalStreams(), this.constraints); - checkTrackCounts('remote', this._pc.getRemoteStreams(), remoteConstraints); - }); - return timerGuard(checkPromise, 60000, "onaddstream never fired"); + if (expectedRemoteTracks > (self.onAddStreamAudioCounter + + self.onAddStreamVideoCounter)) { + // This installs a callback handler for every time onaddstrem fires. + // We rely on the outer mochitest timeout to catch the case where + // onaddstream never fires + self.addStreamCallbacks.checkMediaTracks = function() { + _compareReceivedAndExpectedTracks(constraintsRemote, onSuccess); + }; + } + _compareReceivedAndExpectedTracks(constraintsRemote, onSuccess); + }, - checkMsids: function() { - var checkSdpForMsids = (desc, streams, side) => { - streams.forEach(stream => { - stream.getTracks().forEach(track => { - // TODO(bug 1089798): Once DOMMediaStream has an id field, we - // should be verifying that the SDP contains - // a=msid:<stream-id> <track-id> - ok(desc.sdp.match(new RegExp("a=msid:[^ ]+ " + track.id)), - side + " SDP contains track id " + track.id ); - }); - }); - }; - - checkSdpForMsids(this.localDescription, this._pc.getLocalStreams(), - "local"); - checkSdpForMsids(this.remoteDescription, this._pc.getRemoteStreams(), - "remote"); - }, - - verifySdp: function(desc, expectedType, offerConstraintsList, offerOptions, isLocal) { + verifySdp : function PCW_verifySdp(desc, expectedType, offerConstraintsList, + offerOptions, trickleIceCallback) { info("Examining this SessionDescription: " + JSON.stringify(desc)); info("offerConstraintsList: " + JSON.stringify(offerConstraintsList)); info("offerOptions: " + JSON.stringify(offerOptions)); ok(desc, "SessionDescription is not null"); is(desc.type, expectedType, "SessionDescription type is " + expectedType); ok(desc.sdp.length > 10, "SessionDescription body length is plausible"); ok(desc.sdp.contains("a=ice-ufrag"), "ICE username is present in SDP"); ok(desc.sdp.contains("a=ice-pwd"), "ICE password is present in SDP"); ok(desc.sdp.contains("a=fingerprint"), "ICE fingerprint is present in SDP"); //TODO: update this for loopback support bug 1027350 ok(!desc.sdp.contains(LOOPBACK_ADDR), "loopback interface is absent from SDP"); - var requiresTrickleIce = !desc.sdp.contains("a=candidate"); - if (requiresTrickleIce) { - info("at least one ICE candidate is present in SDP"); + if (desc.sdp.contains("a=candidate")) { + ok(true, "at least one ICE candidate is present in SDP"); + trickleIceCallback(false); } else { info("No ICE candidate in SDP -> requiring trickle ICE"); + trickleIceCallback(true); } - if (isLocal) { - this.localRequiresTrickleIce = requiresTrickleIce; - } else { - this.remoteRequiresTrickleIce = requiresTrickleIce; - } - //TODO: how can we check for absence/presence of m=application? var audioTracks = - this.countTracksInConstraint('audio', offerConstraintsList) || + this.countAudioTracksInMediaConstraint(offerConstraintsList) || this.audioInOfferOptions(offerOptions); info("expected audio tracks: " + audioTracks); if (audioTracks == 0) { ok(!desc.sdp.contains("m=audio"), "audio m-line is absent from SDP"); } else { ok(desc.sdp.contains("m=audio"), "audio m-line is present in SDP"); ok(desc.sdp.contains("a=rtpmap:109 opus/48000/2"), "OPUS codec is present in SDP"); //TODO: ideally the rtcp-mux should be for the m=audio, and not just // anywhere in the SDP (JS SDP parser bug 1045429) ok(desc.sdp.contains("a=rtcp-mux"), "RTCP Mux is offered in SDP"); + } var videoTracks = - this.countTracksInConstraint('video', offerConstraintsList) || + this.countVideoTracksInMediaConstraint(offerConstraintsList) || this.videoInOfferOptions(offerOptions); info("expected video tracks: " + videoTracks); if (videoTracks == 0) { ok(!desc.sdp.contains("m=video"), "video m-line is absent from SDP"); } else { ok(desc.sdp.contains("m=video"), "video m-line is present in SDP"); if (this.h264) { @@ -1424,45 +2376,69 @@ PeerConnectionWrapper.prototype = { ok(desc.sdp.contains("a=rtcp-mux"), "RTCP Mux is offered in SDP"); } }, /** * Check that media flow is present on all media elements involved in this * test by waiting for confirmation that media flow is present. + * + * @param {Function} onSuccess the success callback when media flow + * is confirmed on all media elements */ - checkMediaFlowPresent : function() { - return Promise.all(this.mediaCheckers.map(checker => checker.waitForMediaFlow())); + checkMediaFlowPresent : function PCW_checkMediaFlowPresent(onSuccess) { + var self = this; + + function _checkMediaFlowPresent(index, onSuccess) { + if(index >= self.mediaCheckers.length) { + onSuccess(); + } else { + var mediaChecker = self.mediaCheckers[index]; + mediaChecker.waitForMediaFlow(function() { + _checkMediaFlowPresent(index + 1, onSuccess); + }); + } + } + + _checkMediaFlowPresent(0, onSuccess); }, /** * Check that stats are present by checking for known stats. + * + * @param {Function} onSuccess the success callback to return stats to */ - getStats : function(selector) { - return this._pc.getStats(selector).then(stats => { - info(this + ": Got stats: " + JSON.stringify(stats)); - this._last_stats = stats; - return stats; - }); + getStats : function PCW_getStats(selector, onSuccess) { + var self = this; + + this._pc.getStats(selector, function(stats) { + info(self + ": Got stats: " + JSON.stringify(stats)); + self._last_stats = stats; + onSuccess(stats); + }, generateErrorCallback()); }, /** * Checks that we are getting the media streams we expect. * * @param {object} stats * The stats to check from this PeerConnectionWrapper */ - checkStats : function(stats, twoMachines) { - var toNum = obj => obj? obj : 0; - var numTracks = streams => - streams.reduce((count, stream) => count + - stream.getAudioTracks().length + - stream.getVideoTracks().length, - 0); + checkStats : function PCW_checkStats(stats, twoMachines) { + function toNum(obj) { + return obj? obj : 0; + } + function numTracks(streams) { + var n = 0; + streams.forEach(function(stream) { + n += stream.getAudioTracks().length + stream.getVideoTracks().length; + }); + return n; + } const isWinXP = navigator.userAgent.indexOf("Windows NT 5.1") != -1; // Use spec way of enumerating stats var counters = {}; for (var key in stats) { if (stats.hasOwnProperty(key)) { var res = stats[key]; @@ -1537,17 +2513,17 @@ PeerConnectionWrapper.prototype = { break; } } } } // Use MapClass way of enumerating stats var counters2 = {}; - stats.forEach(res => { + stats.forEach(function(res) { if (!res.isRemote) { counters2[res.type] = toNum(counters2[res.type]) + 1; } }); is(JSON.stringify(counters), JSON.stringify(counters2), "Spec and MapClass variant of RTCStatsReport enumeration agree"); var nin = numTracks(this._pc.getRemoteStreams()); var nout = numTracks(this._pc.getLocalStreams()); @@ -1573,20 +2549,21 @@ PeerConnectionWrapper.prototype = { /** * Compares the Ice server configured for this PeerConnectionWrapper * with the ICE candidates received in the RTCP stats. * * @param {object} stats * The stats to be verified for relayed vs. direct connection. */ - checkStatsIceConnectionType : function(stats) { + checkStatsIceConnectionType : function PCW_checkStatsIceConnectionType(stats) + { var lId; var rId; - Object.keys(stats).forEach(name => { + Object.keys(stats).forEach(function(name) { if ((stats[name].type === "candidatepair") && (stats[name].selected)) { lId = stats[name].localCandidateId; rId = stats[name].remoteCandidateId; } }); info("checkStatsIceConnectionType verifying: local=" + JSON.stringify(stats[lId]) + " remote=" + JSON.stringify(stats[rId])); @@ -1619,36 +2596,36 @@ PeerConnectionWrapper.prototype = { * * @param {object} stats * The stats to check for ICE candidate pairs * @param {object} counters * The counters for media and data tracks based on constraints * @param {object} answer * The SDP answer to check for SDP bundle support */ - checkStatsIceConnections : function(stats, + checkStatsIceConnections : function PCW_checkStatsIceConnections(stats, offerConstraintsList, offerOptions, answer) { var numIceConnections = 0; - Object.keys(stats).forEach(key => { + Object.keys(stats).forEach(function(key) { if ((stats[key].type === "candidatepair") && stats[key].selected) { numIceConnections += 1; } }); info("ICE connections according to stats: " + numIceConnections); if (answer.sdp.contains('a=group:BUNDLE')) { is(numIceConnections, 1, "stats reports exactly 1 ICE connection"); } else { // This code assumes that no media sections have been rejected due to // codec mismatch or other unrecoverable negotiation failures. var numAudioTracks = - this.countTracksInConstraint('audio', offerConstraintsList) || + this.countAudioTracksInMediaConstraint(offerConstraintsList) || this.audioInOfferOptions(offerOptions); var numVideoTracks = - this.countTracksInConstraint('video', offerConstraintsList) || + this.countVideoTracksInMediaConstraint(offerConstraintsList) || this.videoInOfferOptions(offerOptions); var numDataTracks = this.dataChannels.length; var numAudioVideoDataTracks = numAudioTracks + numVideoTracks + numDataTracks; info("expected audio + video + data tracks: " + numAudioVideoDataTracks); is(numAudioVideoDataTracks, numIceConnections, "stats ICE connections matches expected A/V tracks"); } @@ -1658,17 +2635,17 @@ PeerConnectionWrapper.prototype = { * Property-matching function for finding a certain stat in passed-in stats * * @param {object} stats * The stats to check from this PeerConnectionWrapper * @param {object} props * The properties to look for * @returns {boolean} Whether an entry containing all match-props was found. */ - hasStat : function(stats, props) { + hasStat : function PCW_hasStat(stats, props) { for (var key in stats) { if (stats.hasOwnProperty(key)) { var res = stats[key]; var match = true; for (var prop in props) { if (res[prop] !== props[prop]) { match = false; break; @@ -1680,53 +2657,39 @@ PeerConnectionWrapper.prototype = { } } return false; }, /** * Closes the connection */ - close : function() { + close : function PCW_close() { + this._ice_candidates_to_add = []; this._pc.close(); - this.localMediaElements.forEach(e => e.pause()); info(this + ": Closed connection."); }, /** + * Register all events during the setup of the data channel + * + * @param {Function} onDataChannelOpened + * Callback to execute when the data channel has been opened + */ + registerDataChannelOpenEvents : function (onDataChannelOpened) { + info(this + ": Register callback for 'ondatachannel'"); + + this.ondatachannel = function (targetChannel) { + this.dataChannels.push(targetChannel); + info(this + ": 'ondatachannel' fired, registering 'onopen' callback"); + targetChannel.onopen = onDataChannelOpened; + }; + }, + + /** * Returns the string representation of the class * * @returns {String} The string representation */ - toString : function() { + toString : function PCW_toString() { return "PeerConnectionWrapper (" + this.label + ")"; } }; - -// haxx to prevent SimpleTest from failing at window.onload -function addLoadEvent() {} - -var scriptsReady = Promise.all([ - "/tests/SimpleTest/SimpleTest.js", - "head.js", - "templates.js", - "turnConfig.js", - "dataChannel.js", - "network.js" -].map(script => { - var el = document.createElement("script"); - if (typeof scriptRelativePath === 'string' && script.charAt(0) !== '/') { - script = scriptRelativePath + script; - } - el.src = script; - document.head.appendChild(el); - return new Promise(r => { el.onload = r; el.onerror = r; }); -})); - -function createHTML(options) { - return scriptsReady.then(() => realCreateHTML(options)); -} - -function runNetworkTest(testFunction) { - return scriptsReady - .then(() => startNetworkAndTest()) - .then(() => runTestWhenReady(testFunction)); -}
--- a/dom/media/tests/mochitest/steeplechase.ini +++ b/dom/media/tests/mochitest/steeplechase.ini @@ -1,10 +1,9 @@ [DEFAULT] support-files = head.js mediaStreamPlayback.js - network.js pc.js templates.js turnConfig.js [test_peerConnection_basicAudio.html]
--- a/dom/media/tests/mochitest/templates.js +++ b/dom/media/tests/mochitest/templates.js @@ -57,407 +57,668 @@ function dumpSdp(test) { if ((test.pcLocal) && (test.pcRemote) && (typeof test.pcRemote.setLocalDescDate !== 'undefined') && (typeof test.pcRemote.setLocalDescStableEventDate !== 'undefined')) { var delta = deltaSeconds(test.pcRemote.setLocalDescDate, test.pcRemote.setLocalDescStableEventDate); dump("Delay between pcRemote.setLocal <-> pcRemote.signalingStateStable: " + delta + "\n"); } } -function waitForIceConnected(test, pc) { - if (pc.isIceConnected()) { - info(pc + ": ICE connection state log: " + pc.iceConnectionLog); - ok(true, pc + ": ICE is in connected state"); - return Promise.resolve(); - } - - if (!pc.isIceConnectionPending()) { - dumpSdp(test); - var details = pc + ": ICE is already in bad state: " + pc.iceConnectionState; - ok(false, details); - return Promise.reject(new Error(details)); - } - - return pc.waitForIceConnected() - .then(() => { - info(pc + ": ICE connection state log: " + pc.iceConnectionLog); - ok(pc.isIceConnected(), pc + ": ICE switched to 'connected' state"); - }); -} - -// We need to verify that at least one candidate has been (or will be) gathered. -function waitForAnIceCandidate(pc) { - return new Promise(resolve => { - if (!pc.localRequiresTrickleIce || - pc._local_ice_candidates.length > 0) { - resolve(); - } else { - // In some circumstances, especially when both PCs are on the same - // browser, even though we are connected, the connection can be - // established without receiving a single candidate from one or other - // peer. So we wait for at least one... - pc._pc.addEventListener('icecandidate', resolve); +var commandsPeerConnection = [ + [ + 'PC_SETUP_SIGNALING_CLIENT', + function (test) { + if (test.steeplechase) { + test.setTimeout(30000); + test.setupSignalingClient(); + test.registerSignalingCallback("ice_candidate", function (message) { + var pc = test.pcRemote ? test.pcRemote : test.pcLocal; + pc.storeOrAddIceCandidate(new mozRTCIceCandidate(message.ice_candidate)); + }); + test.registerSignalingCallback("end_of_trickle_ice", function (message) { + test.signalingMessagesFinished(); + }); + } + test.next(); + } + ], + [ + 'PC_LOCAL_SETUP_ICE_LOGGER', + function (test) { + test.pcLocal.logIceConnectionState(); + test.next(); } - }).then(() => { - ok(pc._local_ice_candidates.length > 0, - pc + " received local trickle ICE candidates"); - isnot(pc._pc.iceGatheringState, GATH_NEW, - pc + " ICE gathering state is not 'new'"); - }); -} - -function checkTrackStats(pc, audio, outbound) { - var stream = outbound ? pc._pc.getLocalStreams()[0] : pc._pc.getRemoteStreams()[0]; - if (!stream) { - return Promise.resolve(); - } - var track = audio ? stream.getAudioTracks()[0] : stream.getVideoTracks()[0]; - if (!track) { - return Promise.resolve(); - } - var msg = pc + " stats " + (outbound ? "outbound " : "inbound ") + - (audio ? "audio" : "video") + " rtp "; - return pc.getStats(track).then(stats => { - ok(pc.hasStat(stats, { - type: outbound ? "outboundrtp" : "inboundrtp", - isRemote: false, - mediaType: audio ? "audio" : "video" - }), msg + "1"); - ok(!pc.hasStat(stats, { - type: outbound ? "inboundrtp" : "outboundrtp", - isRemote: false - }), msg + "2"); - ok(!pc.hasStat(stats, { - mediaType: audio ? "video" : "audio" - }), msg + "3"); - }); -} - -// checks all stats combinations inbound/outbound, audio/video -var checkAllTrackStats = pc => - Promise.all([0, 1, 2, 3].map(i => checkTrackStats(pc, i & 1, i & 2))); - -var commandsPeerConnection = [ - function PC_SETUP_SIGNALING_CLIENT(test) { - if (test.steeplechase) { - setTimeout(() => { - ok(false, "PeerConnectionTest timed out"); - test.teardown(); - }, 30000); - test.setupSignalingClient(); - test.registerSignalingCallback("ice_candidate", function (message) { - var pc = test.pcRemote ? test.pcRemote : test.pcLocal; - pc.storeOrAddIceCandidate(new mozRTCIceCandidate(message.ice_candidate)); + ], + [ + 'PC_REMOTE_SETUP_ICE_LOGGER', + function (test) { + test.pcRemote.logIceConnectionState(); + test.next(); + } + ], + [ + 'PC_LOCAL_SETUP_SIGNALING_LOGGER', + function (test) { + test.pcLocal.logSignalingState(); + test.next(); + } + ], + [ + 'PC_REMOTE_SETUP_SIGNALING_LOGGER', + function (test) { + test.pcRemote.logSignalingState(); + test.next(); + } + ], + [ + 'PC_LOCAL_GUM', + function (test) { + test.pcLocal.getAllUserMedia(test.pcLocal.constraints, function () { + test.next(); }); - test.registerSignalingCallback("end_of_trickle_ice", function (message) { - test.signalingMessagesFinished(); + } + ], + [ + 'PC_REMOTE_GUM', + function (test) { + test.pcRemote.getAllUserMedia(test.pcRemote.constraints, function () { + test.next(); }); } - }, - - function PC_LOCAL_SETUP_ICE_LOGGER(test) { - test.pcLocal.logIceConnectionState(); - }, - - function PC_REMOTE_SETUP_ICE_LOGGER(test) { - test.pcRemote.logIceConnectionState(); - }, - - function PC_LOCAL_SETUP_SIGNALING_LOGGER(test) { - test.pcLocal.logSignalingState(); - }, - - function PC_REMOTE_SETUP_SIGNALING_LOGGER(test) { - test.pcRemote.logSignalingState(); - }, - - function PC_LOCAL_GUM(test) { - return test.pcLocal.getAllUserMedia(test.pcLocal.constraints); - }, - - function PC_REMOTE_GUM(test) { - return test.pcRemote.getAllUserMedia(test.pcRemote.constraints); - }, - - function PC_LOCAL_CHECK_INITIAL_SIGNALINGSTATE(test) { - is(test.pcLocal.signalingState, STABLE, - "Initial local signalingState is 'stable'"); - }, - - function PC_REMOTE_CHECK_INITIAL_SIGNALINGSTATE(test) { - is(test.pcRemote.signalingState, STABLE, - "Initial remote signalingState is 'stable'"); - }, - - function PC_LOCAL_CHECK_INITIAL_ICE_STATE(test) { - is(test.pcLocal.iceConnectionState, ICE_NEW, - "Initial local ICE connection state is 'new'"); - }, - - function PC_REMOTE_CHECK_INITIAL_ICE_STATE(test) { - is(test.pcRemote.iceConnectionState, ICE_NEW, - "Initial remote ICE connection state is 'new'"); - }, - - function PC_LOCAL_SETUP_ICE_HANDLER(test) { - test.pcLocal.setupIceCandidateHandler(test); - if (test.steeplechase) { - test.pcLocal.endOfTrickleIce.then(() => { - send_message({"type": "end_of_trickle_ice"}); - }); + ], + [ + 'PC_LOCAL_CHECK_INITIAL_SIGNALINGSTATE', + function (test) { + is(test.pcLocal.signalingState, STABLE, + "Initial local signalingState is 'stable'"); + test.next(); + } + ], + [ + 'PC_REMOTE_CHECK_INITIAL_SIGNALINGSTATE', + function (test) { + is(test.pcRemote.signalingState, STABLE, + "Initial remote signalingState is 'stable'"); + test.next(); + } + ], + [ + 'PC_LOCAL_CHECK_INITIAL_ICE_STATE', + function (test) { + is(test.pcLocal.iceConnectionState, ICE_NEW, + "Initial local ICE connection state is 'new'"); + test.next(); } - }, - - function PC_REMOTE_SETUP_ICE_HANDLER(test) { - test.pcRemote.setupIceCandidateHandler(test); - if (test.steeplechase) { - test.pcRemote.endOfTrickleIce.then(() => { - send_message({"type": "end_of_trickle_ice"}); + ], + [ + 'PC_REMOTE_CHECK_INITIAL_ICE_STATE', + function (test) { + is(test.pcRemote.iceConnectionState, ICE_NEW, + "Initial remote ICE connection state is 'new'"); + test.next(); + } + ], + [ + 'PC_LOCAL_SETUP_ICE_HANDLER', + function (test) { + test.pcLocal.setupIceCandidateHandler(test); + test.next(); + } + ], + [ + 'PC_REMOTE_SETUP_ICE_HANDLER', + function (test) { + test.pcRemote.setupIceCandidateHandler(test); + test.next(); + } + ], + [ + 'PC_LOCAL_CREATE_OFFER', + function (test) { + test.createOffer(test.pcLocal, function (offer) { + is(test.pcLocal.signalingState, STABLE, + "Local create offer does not change signaling state"); + test.next(); }); } - }, - - function PC_LOCAL_CREATE_OFFER(test) { - return test.createOffer(test.pcLocal).then(offer => { - is(test.pcLocal.signalingState, STABLE, - "Local create offer does not change signaling state"); - }); - }, - - function PC_LOCAL_STEEPLECHASE_SIGNAL_OFFER(test) { - if (test.steeplechase) { - send_message({"type": "offer", - "offer": test.originalOffer, - "offer_constraints": test.pcLocal.constraints, - "offer_options": test.pcLocal.offerOptions}); - test._local_offer = test.originalOffer; - test._offer_constraints = test.pcLocal.constraints; - test._offer_options = test.pcLocal.offerOptions; + ], + [ + 'PC_LOCAL_STEEPLECHASE_SIGNAL_OFFER', + function (test) { + if (test.steeplechase) { + send_message({"type": "offer", + "offer": test.originalOffer, + "offer_constraints": test.pcLocal.constraints, + "offer_options": test.pcLocal.offerOptions}); + test._local_offer = test.originalOffer; + test._offer_constraints = test.pcLocal.constraints; + test._offer_options = test.pcLocal.offerOptions; + } + test.next(); } - }, - - function PC_LOCAL_SET_LOCAL_DESCRIPTION(test) { - return test.setLocalDescription(test.pcLocal, test.originalOffer, HAVE_LOCAL_OFFER) - .then(() => { + ], + [ + 'PC_LOCAL_SET_LOCAL_DESCRIPTION', + function (test) { + test.setLocalDescription(test.pcLocal, test.originalOffer, HAVE_LOCAL_OFFER, function () { is(test.pcLocal.signalingState, HAVE_LOCAL_OFFER, "signalingState after local setLocalDescription is 'have-local-offer'"); + test.next(); }); - }, - - function PC_REMOTE_GET_OFFER(test) { - if (!test.steeplechase) { - test._local_offer = test.originalOffer; - test._offer_constraints = test.pcLocal.constraints; - test._offer_options = test.pcLocal.offerOptions; - return Promise.resolve(); } - return test.getSignalingMessage("offer") - .then(message => { - ok("offer" in message, "Got an offer message"); - test._local_offer = new mozRTCSessionDescription(message.offer); - test._offer_constraints = message.offer_constraints; - test._offer_options = message.offer_options; - }); - }, - - function PC_REMOTE_SET_REMOTE_DESCRIPTION(test) { - return test.setRemoteDescription(test.pcRemote, test._local_offer, HAVE_REMOTE_OFFER) - .then(() => { + ], + [ + 'PC_REMOTE_GET_OFFER', + function (test) { + if (!test.steeplechase) { + test._local_offer = test.originalOffer; + test._offer_constraints = test.pcLocal.constraints; + test._offer_options = test.pcLocal.offerOptions; + test.next(); + } else { + test.getSignalingMessage("offer", function (message) { + ok("offer" in message, "Got an offer message"); + test._local_offer = new mozRTCSessionDescription(message.offer); + test._offer_constraints = message.offer_constraints; + test._offer_options = message.offer_options; + test.next(); + }); + } + } + ], + [ + 'PC_REMOTE_SET_REMOTE_DESCRIPTION', + function (test) { + test.setRemoteDescription(test.pcRemote, test._local_offer, HAVE_REMOTE_OFFER, function () { is(test.pcRemote.signalingState, HAVE_REMOTE_OFFER, "signalingState after remote setRemoteDescription is 'have-remote-offer'"); + test.next(); }); - }, - - function PC_LOCAL_SANE_LOCAL_SDP(test) { - test.pcLocal.verifySdp(test._local_offer, "offer", - test._offer_constraints, test._offer_options, - true); - }, - - function PC_REMOTE_SANE_REMOTE_SDP(test) { - test.pcRemote.verifySdp(test._local_offer, "offer", - test._offer_constraints, test._offer_options, - false); - }, - - function PC_REMOTE_CREATE_ANSWER(test) { - return test.createAnswer(test.pcRemote) - .then(answer => { + } + ], + [ + 'PC_LOCAL_SANE_LOCAL_SDP', + function (test) { + test.pcLocal.verifySdp(test._local_offer, "offer", + test._offer_constraints, test._offer_options, + function(trickle) { + test.pcLocal.localRequiresTrickleIce = trickle; + }); + test.next(); + } + ], + [ + 'PC_REMOTE_SANE_REMOTE_SDP', + function (test) { + test.pcRemote.verifySdp(test._local_offer, "offer", + test._offer_constraints, test._offer_options, + function (trickle) { + test.pcRemote.remoteRequiresTrickleIce = trickle; + }); + test.next(); + } + ], + [ + 'PC_REMOTE_CREATE_ANSWER', + function (test) { + test.createAnswer(test.pcRemote, function (answer) { is(test.pcRemote.signalingState, HAVE_REMOTE_OFFER, "Remote createAnswer does not change signaling state"); if (test.steeplechase) { send_message({"type": "answer", "answer": test.originalAnswer, "answer_constraints": test.pcRemote.constraints}); test._remote_answer = test.pcRemote._last_answer; test._answer_constraints = test.pcRemote.constraints; } + test.next(); }); - }, - - function PC_REMOTE_CHECK_FOR_DUPLICATED_PORTS_IN_SDP(test) { - var re = /a=candidate.* (UDP|TCP) [\d]+ ([\d\.]+) ([\d]+) typ host/g; + } + ], + [ + 'PC_REMOTE_CHECK_FOR_DUPLICATED_PORTS_IN_SDP', + function (test) { + var re = /a=candidate.* (UDP|TCP) [\d]+ ([\d\.]+) ([\d]+) typ host/g; - var _sdpCandidatesIntoArray = sdp => { - var regexArray = []; - var resultArray = []; - while ((regexArray = re.exec(sdp)) !== null) { - info("regexArray: " + regexArray); - if ((regexArray[1] === "TCP") && (regexArray[3] === "9")) { - // As both sides can advertise TCP active connection on port 9 lets - // ignore them all together - info("Ignoring TCP candidate on port 9"); - continue; + function _sdpCandidatesIntoArray(sdp) { + var regexArray = []; + var resultArray = []; + while ((regexArray = re.exec(sdp)) !== null) { + info("regexArray: " + regexArray); + if ((regexArray[1] === "TCP") && (regexArray[3] === "9")) { + // As both sides can advertise TCP active connection on port 9 lets + // ignore them all together + info("Ignoring TCP candidate on port 9"); + continue; + } + const triple = regexArray[1] + ":" + regexArray[2] + ":" + regexArray[3]; + info("triple: " + triple); + if (resultArray.indexOf(triple) !== -1) { + dump("SDP: " + sdp.replace(/[\r]/g, '') + "\n"); + ok(false, "This Transport:IP:Port " + triple + " appears twice in the SDP above!"); + } + resultArray.push(triple); } - var triple = regexArray[1] + ":" + regexArray[2] + ":" + regexArray[3]; - info("triple: " + triple); - if (resultArray.indexOf(triple) !== -1) { - dump("SDP: " + sdp.replace(/[\r]/g, '') + "\n"); - ok(false, "This Transport:IP:Port " + triple + " appears twice in the SDP above!"); + return resultArray; + } + + const offerTriples = _sdpCandidatesIntoArray(test._local_offer.sdp); + info("Offer ICE host candidates: " + JSON.stringify(offerTriples)); + + const answerTriples = _sdpCandidatesIntoArray(test.originalAnswer.sdp); + info("Answer ICE host candidates: " + JSON.stringify(answerTriples)); + + for (var i=0; i< offerTriples.length; i++) { + if (answerTriples.indexOf(offerTriples[i]) !== -1) { + dump("SDP offer: " + test._local_offer.sdp.replace(/[\r]/g, '') + "\n"); + dump("SDP answer: " + test.originalAnswer.sdp.replace(/[\r]/g, '') + "\n"); + ok(false, "This IP:Port " + offerTriples[i] + " appears in SDP offer and answer!"); } - resultArray.push(triple); } - return resultArray; - }; - var offerTriples = _sdpCandidatesIntoArray(test._local_offer.sdp); - info("Offer ICE host candidates: " + JSON.stringify(offerTriples)); - - var answerTriples = _sdpCandidatesIntoArray(test.originalAnswer.sdp); - info("Answer ICE host candidates: " + JSON.stringify(answerTriples)); - - offerTriples.forEach(o => { - if (answerTriples.indexOf(o) !== -1) { - dump("SDP offer: " + test._local_offer.sdp.replace(/[\r]/g, '') + "\n"); - dump("SDP answer: " + test.originalAnswer.sdp.replace(/[\r]/g, '') + "\n"); - ok(false, "This IP:Port " + o + " appears in SDP offer and answer!"); + test.next(); + } + ], + [ + 'PC_REMOTE_SET_LOCAL_DESCRIPTION', + function (test) { + test.setLocalDescription(test.pcRemote, test.originalAnswer, STABLE, + function () { + is(test.pcRemote.signalingState, STABLE, + "signalingState after remote setLocalDescription is 'stable'"); + test.next(); + } + ); + } + ], + [ + 'PC_LOCAL_GET_ANSWER', + function (test) { + if (!test.steeplechase) { + test._remote_answer = test.originalAnswer; + test._answer_constraints = test.pcRemote.constraints; + test.next(); + } else { + test.getSignalingMessage("answer", function (message) { + ok("answer" in message, "Got an answer message"); + test._remote_answer = new mozRTCSessionDescription(message.answer); + test._answer_constraints = message.answer_constraints; + test.next(); + }); } - }); - }, - - function PC_REMOTE_SET_LOCAL_DESCRIPTION(test) { - return test.setLocalDescription(test.pcRemote, test.originalAnswer, STABLE) - .then(() => { - is(test.pcRemote.signalingState, STABLE, - "signalingState after remote setLocalDescription is 'stable'"); - }); - }, - - function PC_LOCAL_GET_ANSWER(test) { - if (!test.steeplechase) { - test._remote_answer = test.originalAnswer; - test._answer_constraints = test.pcRemote.constraints; - return Promise.resolve(); + } + ], + [ + 'PC_LOCAL_SET_REMOTE_DESCRIPTION', + function (test) { + test.setRemoteDescription(test.pcLocal, test._remote_answer, STABLE, + function () { + is(test.pcLocal.signalingState, STABLE, + "signalingState after local setRemoteDescription is 'stable'"); + test.next(); + } + ); } + ], + [ + 'PC_REMOTE_SANE_LOCAL_SDP', + function (test) { + test.pcRemote.verifySdp(test._remote_answer, "answer", + test._offer_constraints, test._offer_options, + function (trickle) { + test.pcRemote.localRequiresTrickleIce = trickle; + }); + test.next(); + } + ], + [ + 'PC_LOCAL_SANE_REMOTE_SDP', + function (test) { + test.pcLocal.verifySdp(test._remote_answer, "answer", + test._offer_constraints, test._offer_options, + function (trickle) { + test.pcLocal.remoteRequiresTrickleIce = trickle; + }); + test.next(); + } + ], + [ + 'PC_LOCAL_WAIT_FOR_ICE_CONNECTED', + function (test) { + var myTest = test; + var myPc = myTest.pcLocal; - return test.getSignalingMessage("answer").then(message => { - ok("answer" in message, "Got an answer message"); - test._remote_answer = new mozRTCSessionDescription(message.answer); - test._answer_constraints = message.answer_constraints; - }); - }, + function onIceConnectedSuccess () { + info("pcLocal ICE connection state log: " + test.pcLocal.iceConnectionLog); + ok(true, "pc_local: ICE switched to 'connected' state"); + myTest.next(); + }; + function onIceConnectedFailed () { + dumpSdp(myTest); + ok(false, "pc_local: ICE failed to switch to 'connected' state: " + myPc.iceConnectionState); + myTest.next(); + }; - function PC_LOCAL_SET_REMOTE_DESCRIPTION(test) { - test.setRemoteDescription(test.pcLocal, test._remote_answer, STABLE) - .then(() => { - is(test.pcLocal.signalingState, STABLE, - "signalingState after local setRemoteDescription is 'stable'"); - }); - }, - function PC_REMOTE_SANE_LOCAL_SDP(test) { - test.pcRemote.verifySdp(test._remote_answer, "answer", - test._offer_constraints, test._offer_options, - true); - }, - function PC_LOCAL_SANE_REMOTE_SDP(test) { - test.pcLocal.verifySdp(test._remote_answer, "answer", - test._offer_constraints, test._offer_options, - false); - }, + if (myPc.isIceConnected()) { + info("pcLocal ICE connection state log: " + test.pcLocal.iceConnectionLog); + ok(true, "pc_local: ICE is in connected state"); + myTest.next(); + } else if (myPc.isIceConnectionPending()) { + myPc.waitForIceConnected(onIceConnectedSuccess, onIceConnectedFailed); + } else { + dumpSdp(myTest); + ok(false, "pc_local: ICE is already in bad state: " + myPc.iceConnectionState); + myTest.next(); + } + } + ], + [ + 'PC_LOCAL_VERIFY_ICE_GATHERING', + function (test) { + if (test.pcLocal.localRequiresTrickleIce) { + ok(test.pcLocal._local_ice_candidates.length > 0, "Received local trickle ICE candidates"); + } + isnot(test.pcLocal._pc.iceGatheringState, GATH_NEW, "ICE gathering state is not 'new'"); + test.next(); + } + ], + [ + 'PC_REMOTE_WAIT_FOR_ICE_CONNECTED', + function (test) { + var myTest = test; + var myPc = myTest.pcRemote; + + function onIceConnectedSuccess () { + info("pcRemote ICE connection state log: " + test.pcRemote.iceConnectionLog); + ok(true, "pc_remote: ICE switched to 'connected' state"); + myTest.next(); + }; + function onIceConnectedFailed () { + dumpSdp(myTest); + ok(false, "pc_remote: ICE failed to switch to 'connected' state: " + myPc.iceConnectionState); + myTest.next(); + }; - function PC_LOCAL_WAIT_FOR_ICE_CONNECTED(test) { - return waitForIceConnected(test, test.pcLocal); - }, - - function PC_REMOTE_WAIT_FOR_ICE_CONNECTED(test) { - return waitForIceConnected(test, test.pcRemote); - }, - - function PC_LOCAL_VERIFY_ICE_GATHERING(test) { - return waitForAnIceCandidate(test.pcLocal); - }, - - function PC_REMOTE_VERIFY_ICE_GATHERING(test) { - return waitForAnIceCandidate(test.pcRemote); - }, - - function PC_LOCAL_CHECK_MEDIA_TRACKS(test) { - return test.pcLocal.checkMediaTracks(test._answer_constraints); - }, - - function PC_REMOTE_CHECK_MEDIA_TRACKS(test) { - return test.pcRemote.checkMediaTracks(test._offer_constraints); - }, - - function PC_LOCAL_CHECK_MEDIA_FLOW_PRESENT(test) { - return test.pcLocal.checkMediaFlowPresent(); - }, + if (myPc.isIceConnected()) { + info("pcRemote ICE connection state log: " + test.pcRemote.iceConnectionLog); + ok(true, "pc_remote: ICE is in connected state"); + myTest.next(); + } else if (myPc.isIceConnectionPending()) { + myPc.waitForIceConnected(onIceConnectedSuccess, onIceConnectedFailed); + } else { + dumpSdp(myTest); + ok(false, "pc_remote: ICE is already in bad state: " + myPc.iceConnectionState); + myTest.next(); + } + } + ], + [ + 'PC_REMOTE_VERIFY_ICE_GATHERING', + function (test) { + if (test.pcRemote.localRequiresTrickleIce) { + ok(test.pcRemote._local_ice_candidates.length > 0, "Received local trickle ICE candidates"); + } + isnot(test.pcRemote._pc.iceGatheringState, GATH_NEW, "ICE gathering state is not 'new'"); + test.next(); + } + ], + [ + 'PC_LOCAL_CHECK_MEDIA_TRACKS', + function (test) { + test.pcLocal.checkMediaTracks(test._answer_constraints, function () { + test.next(); + }); + } + ], + [ + 'PC_REMOTE_CHECK_MEDIA_TRACKS', + function (test) { + test.pcRemote.checkMediaTracks(test._offer_constraints, function () { + test.next(); + }); + } + ], + [ + 'PC_LOCAL_CHECK_MEDIA_FLOW_PRESENT', + function (test) { + test.pcLocal.checkMediaFlowPresent(function () { + test.next(); + }); + } + ], + [ + 'PC_REMOTE_CHECK_MEDIA_FLOW_PRESENT', + function (test) { + test.pcRemote.checkMediaFlowPresent(function () { + test.next(); + }); + } + ], + [ + 'PC_LOCAL_CHECK_STATS', + function (test) { + test.pcLocal.getStats(null, function(stats) { + test.pcLocal.checkStats(stats, test.steeplechase); + test.next(); + }); + } + ], + [ + 'PC_REMOTE_CHECK_STATS', + function (test) { + test.pcRemote.getStats(null, function(stats) { + test.pcRemote.checkStats(stats, test.steeplechase); + test.next(); + }); + } + ], + [ + 'PC_LOCAL_CHECK_ICE_CONNECTION_TYPE', + function (test) { + test.pcLocal.getStats(null, function(stats) { + test.pcLocal.checkStatsIceConnectionType(stats); + test.next(); + }); + } + ], + [ + 'PC_REMOTE_CHECK_ICE_CONNECTION_TYPE', + function (test) { + test.pcRemote.getStats(null, function(stats) { + test.pcRemote.checkStatsIceConnectionType(stats); + test.next(); + }); + } + ], + [ + 'PC_LOCAL_CHECK_ICE_CONNECTIONS', + function (test) { + test.pcLocal.getStats(null, function(stats) { + test.pcLocal.checkStatsIceConnections(stats, + test._offer_constraints, + test._offer_options, + test._remote_answer); + test.next(); + }); + } + ], + [ + 'PC_REMOTE_CHECK_ICE_CONNECTIONS', + function (test) { + test.pcRemote.getStats(null, function(stats) { + test.pcRemote.checkStatsIceConnections(stats, + test._offer_constraints, + test._offer_options, + test.originalAnswer); + test.next(); + }); + } + ], + [ + 'PC_LOCAL_CHECK_GETSTATS_AUDIOTRACK_OUTBOUND', + function (test) { + var pc = test.pcLocal; + var stream = pc._pc.getLocalStreams()[0]; + var track = stream && stream.getAudioTracks()[0]; + if (track) { + var msg = "pcLocal.HasStat outbound audio rtp "; + pc.getStats(track, function(stats) { + ok(pc.hasStat(stats, + { type:"outboundrtp", isRemote:false, mediaType:"audio" }), + msg + "1"); + ok(!pc.hasStat(stats, { type:"inboundrtp", isRemote:false }), msg + "2"); + ok(!pc.hasStat(stats, { mediaType:"video" }), msg + "3"); + test.next(); + }); + } else { + test.next(); + } + } + ], + [ + 'PC_LOCAL_CHECK_GETSTATS_VIDEOTRACK_OUTBOUND', + function (test) { + var pc = test.pcLocal; + var stream = pc._pc.getLocalStreams()[0]; + var track = stream && stream.getVideoTracks()[0]; + if (track) { + var msg = "pcLocal.HasStat outbound video rtp "; + pc.getStats(track, function(stats) { + ok(pc.hasStat(stats, + { type:"outboundrtp", isRemote:false, mediaType:"video" }), + msg + "1"); + ok(!pc.hasStat(stats, { type:"inboundrtp", isRemote:false }), msg + "2"); + ok(!pc.hasStat(stats, { mediaType:"audio" }), msg + "3"); + test.next(); + }); + } else { + test.next(); + } + } + ], + [ + 'PC_LOCAL_CHECK_GETSTATS_AUDIOTRACK_INBOUND', + function (test) { + var pc = test.pcLocal; + var stream = pc._pc.getRemoteStreams()[0]; + var track = stream && stream.getAudioTracks()[0]; + if (track) { + var msg = "pcLocal.HasStat inbound audio rtp "; + pc.getStats(track, function(stats) { + ok(pc.hasStat(stats, + { type:"inboundrtp", isRemote:false, mediaType:"audio" }), + msg + "1"); + ok(!pc.hasStat(stats, { type:"outboundrtp", isRemote:false }), msg + "2"); + ok(!pc.hasStat(stats, { mediaType:"video" }), msg + "3"); + test.next(); + }); + } else { + test.next(); + } + } + ], + [ + 'PC_LOCAL_CHECK_GETSTATS_VIDEOTRACK_INBOUND', + function (test) { + var pc = test.pcLocal; + var stream = pc._pc.getRemoteStreams()[0]; + var track = stream && stream.getVideoTracks()[0]; + if (track) { + var msg = "pcLocal.HasStat inbound video rtp "; + pc.getStats(track, function(stats) { + ok(pc.hasStat(stats, + { type:"inboundrtp", isRemote:false, mediaType:"video" }), + msg + "1"); + ok(!pc.hasStat(stats, { type:"outboundrtp", isRemote:false }), msg + "2"); + ok(!pc.hasStat(stats, { mediaType:"audio" }), msg + "3"); + test.next(); + }); + } else { + test.next(); + } + } + ], + [ + 'PC_REMOTE_CHECK_GETSTATS_AUDIOTRACK_OUTBOUND', + function (test) { + var pc = test.pcRemote; + var stream = pc._pc.getLocalStreams()[0]; + var track = stream && stream.getAudioTracks()[0]; + if (track) { + var msg = "pcRemote.HasStat outbound audio rtp "; + pc.getStats(track, function(stats) { + ok(pc.hasStat(stats, + { type:"outboundrtp", isRemote:false, mediaType:"audio" }), + msg + "1"); + ok(!pc.hasStat(stats, { type:"inboundrtp", isRemote:false }), msg + "2"); + ok(!pc.hasStat(stats, { mediaType:"video" }), msg + "3"); + test.next(); + }); + } else { + test.next(); + } + } + ], + [ + 'PC_REMOTE_CHECK_GETSTATS_VIDEOTRACK_OUTBOUND', + function (test) { + var pc = test.pcRemote; + var stream = pc._pc.getLocalStreams()[0]; + var track = stream && stream.getVideoTracks()[0]; + if (track) { + var msg = "pcRemote.HasStat outbound audio rtp "; + pc.getStats(track, function(stats) { + ok(pc.hasStat(stats, + { type:"outboundrtp", isRemote:false, mediaType:"video" }), + msg + "1"); + ok(!pc.hasStat(stats, { type:"inboundrtp", isRemote:false }), msg + "2"); + ok(!pc.hasStat(stats, { mediaType:"audio" }), msg + "3"); + test.next(); + }); + } else { + test.next(); + } + } + ], + [ + 'PC_REMOTE_CHECK_GETSTATS_AUDIOTRACK_INBOUND', + function (test) { + var pc = test.pcRemote; + var stream = pc._pc.getRemoteStreams()[0]; + var track = stream && stream.getAudioTracks()[0]; + if (track) { + var msg = "pcRemote.HasStat inbound audio rtp "; + pc.getStats(track, function(stats) { + ok(pc.hasStat(stats, + { type:"inboundrtp", isRemote:false, mediaType:"audio" }), + msg + "1"); + ok(!pc.hasStat(stats, { type:"outboundrtp", isRemote:false }), msg + "2"); + ok(!pc.hasStat(stats, { mediaType:"video" }), msg + "3"); + test.next(); + }); + } else { + test.next(); + } + } + ], + [ + 'PC_REMOTE_CHECK_GETSTATS_VIDEOTRACK_INBOUND', + function (test) { + var pc = test.pcRemote; + var stream = pc._pc.getRemoteStreams()[0]; + var track = stream && stream.getVideoTracks()[0]; + if (track) { + var msg = "pcRemote.HasStat inbound video rtp "; + pc.getStats(track, function(stats) { + ok(pc.hasStat(stats, + { type:"inboundrtp", isRemote:false, mediaType:"video" }), + msg + "1"); + ok(!pc.hasStat(stats, { type:"outboundrtp", isRemote:false }), msg + "2"); + ok(!pc.hasStat(stats, { mediaType:"audio" }), msg + "3"); + test.next(); + }); + } else { + test.next(); + } + } + ] +]; - function PC_REMOTE_CHECK_MEDIA_FLOW_PRESENT(test) { - return test.pcRemote.checkMediaFlowPresent(); - }, -/* TODO: re-enable when Bug 1095218 lands - function PC_LOCAL_CHECK_MSID(test) { - test.pcLocal.checkMsids(); - }, - function PC_REMOTE_CHECK_MSID(test) { - test.pcRemote.checkMsids(); - }, -*/ - function PC_LOCAL_CHECK_STATS(test) { - return test.pcLocal.getStats(null).then(stats => { - test.pcLocal.checkStats(stats, test.steeplechase); - }); - }, - - function PC_REMOTE_CHECK_STATS(test) { - test.pcRemote.getStats(null).then(stats => { - test.pcRemote.checkStats(stats, test.steeplechase); - }); - }, - - function PC_LOCAL_CHECK_ICE_CONNECTION_TYPE(test) { - test.pcLocal.getStats(null).then(stats => { - test.pcLocal.checkStatsIceConnectionType(stats); - }); - }, - - function PC_REMOTE_CHECK_ICE_CONNECTION_TYPE(test) { - test.pcRemote.getStats(null).then(stats => { - test.pcRemote.checkStatsIceConnectionType(stats); - }); - }, - - function PC_LOCAL_CHECK_ICE_CONNECTIONS(test) { - test.pcLocal.getStats(null).then(stats => { - test.pcLocal.checkStatsIceConnections(stats, - test._offer_constraints, - test._offer_options, - test._remote_answer); - }); - }, - - function PC_REMOTE_CHECK_ICE_CONNECTIONS(test) { - test.pcRemote.getStats(null).then(stats => { - test.pcRemote.checkStatsIceConnections(stats, - test._offer_constraints, - test._offer_options, - test.originalAnswer); - }); - }, - - function PC_LOCAL_CHECK_STATS(test) { - return checkAllTrackStats(test.pcLocal); - }, - function PC_REMOTE_CHECK_STATS(test) { - return checkAllTrackStats(test.pcRemote); - } -];
--- a/dom/media/tests/mochitest/test_dataChannel_basicAudio.html +++ b/dom/media/tests/mochitest/test_dataChannel_basicAudio.html @@ -1,12 +1,18 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="dataChannel.js"></script> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="pc.js"></script> + <script type="application/javascript" src="templates.js"></script> + <script type="application/javascript" src="turnConfig.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "796895", title: "Basic data channel audio connection" });
--- a/dom/media/tests/mochitest/test_dataChannel_basicAudioVideo.html +++ b/dom/media/tests/mochitest/test_dataChannel_basicAudioVideo.html @@ -1,12 +1,18 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="dataChannel.js"></script> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="pc.js"></script> + <script type="application/javascript" src="templates.js"></script> + <script type="application/javascript" src="turnConfig.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "796891", title: "Basic data channel audio/video connection" });
--- a/dom/media/tests/mochitest/test_dataChannel_basicAudioVideoCombined.html +++ b/dom/media/tests/mochitest/test_dataChannel_basicAudioVideoCombined.html @@ -1,12 +1,18 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="dataChannel.js"></script> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="pc.js"></script> + <script type="application/javascript" src="templates.js"></script> + <script type="application/javascript" src="turnConfig.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "796891", title: "Basic data channel audio/video connection" });
--- a/dom/media/tests/mochitest/test_dataChannel_basicAudioVideoNoBundle.html +++ b/dom/media/tests/mochitest/test_dataChannel_basicAudioVideoNoBundle.html @@ -1,33 +1,44 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="dataChannel.js"></script> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="pc.js"></script> + <script type="application/javascript" src="templates.js"></script> + <script type="application/javascript" src="turnConfig.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "1016476", title: "Basic data channel audio/video connection without bundle" }); -var test; -runNetworkTest(function () { - test = new PeerConnectionTest(); - addInitialDataChannel(test.chain); - test.chain.insertAfter("PC_LOCAL_CREATE_OFFER", [ - function PC_LOCAL_REMOVE_BUNDLE_FROM_OFFER(test) { - // Just replace a=group:BUNDLE with something that will be ignored. - test.originalOffer.sdp = test.originalOffer.sdp.replace( - "a=group:BUNDLE", - "a=foo:"); - } - ]); - test.setMediaConstraints([{audio: true}, {video: true}], - [{audio: true}, {video: true}]); - test.run(); -}); + var test; + runNetworkTest(function () { + test = new PeerConnectionTest(); + addInitialDataChannel(test.chain); + test.chain.insertAfter("PC_LOCAL_CREATE_OFFER", + [[ + 'PC_LOCAL_REMOVE_BUNDLE_FROM_OFFER', + function (test) { + // Just replace a=group:BUNDLE with something that will be ignored. + test.originalOffer.sdp = test.originalOffer.sdp.replace( + "a=group:BUNDLE", + "a=foo:"); + test.next(); + } + ]] + ); + test.setMediaConstraints([{audio: true}, {video: true}], + [{audio: true}, {video: true}]); + test.run(); + }); + </script> </pre> </body> </html>
--- a/dom/media/tests/mochitest/test_dataChannel_basicDataOnly.html +++ b/dom/media/tests/mochitest/test_dataChannel_basicDataOnly.html @@ -1,12 +1,18 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="dataChannel.js"></script> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="pc.js"></script> + <script type="application/javascript" src="templates.js"></script> + <script type="application/javascript" src="turnConfig.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "796894", title: "Basic datachannel only connection" });
--- a/dom/media/tests/mochitest/test_dataChannel_basicVideo.html +++ b/dom/media/tests/mochitest/test_dataChannel_basicVideo.html @@ -1,12 +1,18 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="dataChannel.js"></script> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="pc.js"></script> + <script type="application/javascript" src="templates.js"></script> + <script type="application/javascript" src="turnConfig.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "796889", title: "Basic data channel video connection" });
--- a/dom/media/tests/mochitest/test_dataChannel_bug1013809.html +++ b/dom/media/tests/mochitest/test_dataChannel_bug1013809.html @@ -1,12 +1,18 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="dataChannel.js"></script> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="pc.js"></script> + <script type="application/javascript" src="templates.js"></script> + <script type="application/javascript" src="turnConfig.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "796895", title: "Basic data channel audio connection" });
--- a/dom/media/tests/mochitest/test_dataChannel_noOffer.html +++ b/dom/media/tests/mochitest/test_dataChannel_noOffer.html @@ -1,11 +1,14 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="pc.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "856319", title: "Don't offer m=application unless createDataChannel is called first"
--- a/dom/media/tests/mochitest/test_getUserMedia_basicAudio.html +++ b/dom/media/tests/mochitest/test_getUserMedia_basicAudio.html @@ -1,29 +1,46 @@ <!DOCTYPE HTML> <html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=781534 +--> <head> + <meta charset="utf-8"> + <title>mozGetUserMedia Basic Audio Test</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="mediaStreamPlayback.js"></script> </head> <body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=781534">mozGetUserMedia Basic Audio Test</a> +<p id="display"></p> +<div id="content" style="display: none"> + <audio id="testAudio"></audio> +</div> <pre id="test"> <script type="application/javascript"> - createHTML({ title: "getUserMedia Basic Audio Test", bug: "781534" }); /** * Run a test to verify that we can complete a start and stop media playback * cycle for an audio LocalMediaStream on an audio HTMLMediaElement. */ runTest(function () { - var testAudio = createMediaElement('audio', 'testAudio'); + var testAudio = document.getElementById('testAudio'); var constraints = {audio: true}; - getUserMedia(constraints).then(aStream => { + getUserMedia(constraints, function (aStream) { checkMediaStreamTracks(constraints, aStream); var playback = new LocalMediaStreamPlayback(testAudio, aStream); - return playback.playMedia(false); - }).then(() => SimpleTest.finish(), generateErrorCallback()); + playback.playMedia(false, function () { + aStream.stop(); + SimpleTest.finish(); + }, generateErrorCallback()); + + }, generateErrorCallback()); + }); </script> </pre> </body> </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_basicScreenshare.html +++ b/dom/media/tests/mochitest/test_getUserMedia_basicScreenshare.html @@ -1,45 +1,58 @@ <!DOCTYPE HTML> <html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=983504 +--> <head> + <meta charset="utf-8"> + <title>mozGetUserMedia Basic Screenshare Test</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="mediaStreamPlayback.js"></script> </head> <body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=983504">mozGetUserMedia Basic Screenshare Test</a> +<p id="display"></p> +<div id="content" style="display: none"> + <video id="testVideo"></video> +</div> <pre id="test"> <script type="application/javascript"> - createHTML({ - title: "getUserMedia Basic Screenshare Test", - bug: "983504" - }); /** * Run a test to verify that we can complete a start and stop media playback * cycle for an screenshare LocalMediaStream on a video HTMLMediaElement. */ runTest(function () { const isWinXP = navigator.userAgent.indexOf("Windows NT 5.1") != -1; if (IsMacOSX10_6orOlder() || isWinXP) { ok(true, "Screensharing disabled for OSX10.6 and WinXP"); SimpleTest.finish(); return; } - var testVideo = createMediaElement('video', 'testVideo'); + var testVideo = document.getElementById('testVideo'); var constraints = { video: { mozMediaSource: "screen", mediaSource: "screen" }, fake: false }; - getUserMedia(constraints).then(aStream => { + getUserMedia(constraints, function (aStream) { checkMediaStreamTracks(constraints, aStream); var playback = new LocalMediaStreamPlayback(testVideo, aStream); - return playback.playMediaWithStreamStop(false); - }).then(() => SimpleTest.finish(), generateErrorCallback()); + playback.playMediaWithStreamStop(false, function () { + aStream.stop(); + SimpleTest.finish(); + }, generateErrorCallback()); + + }, generateErrorCallback()); }); </script> </pre> </body> </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_basicVideo.html +++ b/dom/media/tests/mochitest/test_getUserMedia_basicVideo.html @@ -1,32 +1,46 @@ <!DOCTYPE HTML> <html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=781534 +--> <head> + <meta charset="utf-8"> + <title>mozGetUserMedia Basic Video Test</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="mediaStreamPlayback.js"></script> </head> <body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=781534">mozGetUserMedia Basic Video Test</a> +<p id="display"></p> +<div id="content" style="display: none"> + <video id="testVideo"></video> +</div> <pre id="test"> <script type="application/javascript"> - createHTML({ - title: "getUserMedia Basic Video Test", - bug: "781534" - }); /** * Run a test to verify that we can complete a start and stop media playback * cycle for an video LocalMediaStream on a video HTMLMediaElement. */ runTest(function () { - var testVideo = createMediaElement('video', 'testVideo'); + var testVideo = document.getElementById('testVideo'); var constraints = {video: true}; - getUserMedia(constraints).then(aStream => { + getUserMedia(constraints, function (aStream) { checkMediaStreamTracks(constraints, aStream); var playback = new LocalMediaStreamPlayback(testVideo, aStream); - return playback.playMedia(false); - }).then(() => SimpleTest.finish(), generateErrorCallback()); + playback.playMedia(false, function () { + aStream.stop(); + SimpleTest.finish(); + }, generateErrorCallback()); + + }, generateErrorCallback()); + }); </script> </pre> </body> </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_basicVideoAudio.html +++ b/dom/media/tests/mochitest/test_getUserMedia_basicVideoAudio.html @@ -1,32 +1,45 @@ <!DOCTYPE HTML> <html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=781534 +--> <head> + <meta charset="utf-8"> + <title>mozGetUserMedia Basic Video & Audio Test</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="mediaStreamPlayback.js"></script> </head> <body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=781534">mozGetUserMedia Basic Video & Audio Test</a> +<p id="display"></p> +<div id="content" style="display: none"> + <video id="testVideoAudio"></video> +</div> <pre id="test"> <script type="application/javascript"> - createHTML({ - title: "getUserMedia Basic Video & Audio Test", - bug: "781534" - }); /** * Run a test to verify that we can complete a start and stop media playback * cycle for a video and audio LocalMediaStream on a video HTMLMediaElement. */ runTest(function () { - var testVideoAudio = createMediaElement('video', 'testVideoAudio'); + var testVideoAudio = document.getElementById('testVideoAudio'); var constraints = {video: true, audio: true}; - getUserMedia(constraints).then(aStream => { + getUserMedia(constraints, function (aStream) { checkMediaStreamTracks(constraints, aStream); var playback = new LocalMediaStreamPlayback(testVideoAudio, aStream); - return playback.playMedia(false); - }).then(() => SimpleTest.finish(), generateErrorCallback()); + playback.playMedia(false, function () { + aStream.stop(); + SimpleTest.finish(); + }, generateErrorCallback()); + + }, generateErrorCallback()); }); </script> </pre> </body> </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_basicWindowshare.html +++ b/dom/media/tests/mochitest/test_getUserMedia_basicWindowshare.html @@ -1,45 +1,58 @@ <!DOCTYPE HTML> <html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=983504 +--> <head> + <meta charset="utf-8"> + <title>mozGetUserMedia Basic Windowshare Test</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="mediaStreamPlayback.js"></script> </head> <body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1038926">mozGetUserMedia Basic Windowshare Test</a> +<p id="display"></p> +<div id="content" style="display: none"> + <video id="testVideo"></video> +</div> <pre id="test"> <script type="application/javascript"> - createHTML({ - title: "getUserMedia Basic Windowshare Test", - bug: "1038926" - }); /** * Run a test to verify that we can complete a start and stop media playback * cycle for an screenshare LocalMediaStream on a video HTMLMediaElement. */ runTest(function () { const isWinXP = navigator.userAgent.indexOf("Windows NT 5.1") != -1; if (IsMacOSX10_6orOlder() || isWinXP) { ok(true, "Screensharing disabled for OSX10.6 and WinXP"); SimpleTest.finish(); return; } - var testVideo = createMediaElement('video', 'testVideo'); + var testVideo = document.getElementById('testVideo'); var constraints = { video: { mozMediaSource: "window", mediaSource: "window" }, fake: false }; - getUserMedia(constraints).then(aStream => { + getUserMedia(constraints, function (aStream) { checkMediaStreamTracks(constraints, aStream); var playback = new LocalMediaStreamPlayback(testVideo, aStream); - return playback.playMediaWithStreamStop(false); - }).then(() => SimpleTest.finish(), generateErrorCallback()); + playback.playMediaWithStreamStop(false, function () { + aStream.stop(); + SimpleTest.finish(); + }, generateErrorCallback()); + + }, generateErrorCallback()); }); </script> </pre> </body> </html>
deleted file mode 100644 --- a/dom/media/tests/mochitest/test_getUserMedia_callbacks.html +++ /dev/null @@ -1,33 +0,0 @@ -<!DOCTYPE HTML> -<html> -<head> - <script type="application/javascript" src="mediaStreamPlayback.js"></script> -</head> -<body> -<pre id="test"> -<script type="application/javascript"> - createHTML({ - title: "navigator.mozGetUserMedia Callback Test", - bug: "1119593" - }); - /** - * Check that the old fashioned callback-based function works. - */ - runTest(function () { - var testAudio = createMediaElement('audio', 'testAudio'); - var constraints = {audio: true}; - - SimpleTest.waitForExplicitFinish(); - navigator.mozGetUserMedia(constraints, aStream => { - checkMediaStreamTracks(constraints, aStream); - - var playback = new LocalMediaStreamPlayback(testAudio, aStream); - return playback.playMedia(false) - .then(() => SimpleTest.finish(), generateErrorCallback()); - }, generateErrorCallback()); - }); - -</script> -</pre> -</body> -</html>
--- a/dom/media/tests/mochitest/test_getUserMedia_constraints.html +++ b/dom/media/tests/mochitest/test_getUserMedia_constraints.html @@ -1,18 +1,29 @@ <!DOCTYPE HTML> <html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=882145 +--> <head> - <script src="mediaStreamPlayback.js"></script> - <script src="constraints.js"></script> + <meta charset="utf-8"> + <title>Test mozGetUserMedia Constraints</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> + <script type="application/javascript" src="constraints.js"></script> </head> <body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=882145">Test mozGetUserMedia Constraints (desktop)</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> <pre id="test"> <script type="application/javascript"> -createHTML({ title: "Test getUserMedia constraints (desktop)", bug: "882145" }); /** See constraints.js for testConstraints() and common_tests. TODO(jib): Merge desktop and mobile version of these tests again (Bug 997365) */ var desktop_tests = [ { message: "legacy facingMode ignored (desktop)", constraints: { video: { mandatory: { facingMode:'left' } }, fake: true }, error: null },
--- a/dom/media/tests/mochitest/test_getUserMedia_constraints_mobile.html +++ b/dom/media/tests/mochitest/test_getUserMedia_constraints_mobile.html @@ -1,18 +1,29 @@ <!DOCTYPE HTML> <html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=882145 +--> <head> - <script src="mediaStreamPlayback.js"></script> - <script src="constraints.js"></script> + <meta charset="utf-8"> + <title>Test mozGetUserMedia Constraints</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> + <script type="application/javascript" src="constraints.js"></script> </head> <body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=882145">Test mozGetUserMedia Constraints (mobile)</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> <pre id="test"> <script type="application/javascript"> -createHTML({ title: "Test getUserMedia constraints (mobile)", bug: "882145" }); /** See constraints.js for testConstraints() and common_tests. TODO(jib): Merge desktop and mobile version of these tests again (Bug 997365) */ var mobile_tests = [ { message: "legacy facingMode overconstrains video (mobile)", constraints: { video: { mandatory: { facingMode:'left' } }, fake: true }, error: "NotFoundError" },
--- a/dom/media/tests/mochitest/test_getUserMedia_exceptions.html +++ b/dom/media/tests/mochitest/test_getUserMedia_exceptions.html @@ -1,17 +1,28 @@ <!DOCTYPE HTML> <html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=795367 +--> <head> - <script type="application/javascript" src="mediaStreamPlayback.js"></script> + <meta charset="utf-8"> + <title>Test mozGetUserMedia Exceptions</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> </head> <body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=795367">Test mozGetUserMedia Exceptions</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> <pre id="test"> <script type="application/javascript"> -createHTML({ title: "Test mozGetUserMedia Exceptions", bug: "795367" }); /** These tests verify that the appropriate exception is thrown when incorrect values are provided to the immediate mozGetUserMedia call. */ var exceptionTests = [ // Each test here verifies that a caller is required to have all // three arguments in order to call mozGetUserMedia { params: undefined,
--- a/dom/media/tests/mochitest/test_getUserMedia_gumWithinGum.html +++ b/dom/media/tests/mochitest/test_getUserMedia_gumWithinGum.html @@ -1,40 +1,56 @@ -<!DOCTYPE HTML> +<!DOCTYPE HTML> <html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=822109 +--> <head> + <meta charset="utf-8"> + <title>mozGetUserMedia gum within gum</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="mediaStreamPlayback.js"></script> </head> <body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia gum within gum</a> +<p id="display"></p> +<div id="content" style="display: none"> + <video id="testVideo"></video> + <audio id="testAudio"></audio> +</div> <pre id="test"> <script type="application/javascript"> - createHTML({title: "getUserMedia within getUserMedia", bug: "822109" }); /** * Run a test that we can complete a playback cycle for a video, * then upon completion, do a playback cycle with audio, such that * the audio gum call happens within the video gum call. */ runTest(function () { - getUserMedia({video: true}) - .then(videoStream => { - var testVideo = createMediaElement('video', 'testVideo'); - var videoPlayback = new LocalMediaStreamPlayback(testVideo, - videoStream); + getUserMedia({video: true}, function(videoStream) { + var testVideo = document.getElementById('testVideo'); + var videoStreamPlayback = new LocalMediaStreamPlayback(testVideo, + videoStream); + + videoStreamPlayback.playMedia(false, function() { + getUserMedia({audio: true}, function(audioStream) { + var testAudio = document.getElementById('testAudio'); + var audioStreamPlayback = new LocalMediaStreamPlayback(testAudio, + audioStream); - return videoPlayback.playMedia(false) - .then(() => getUserMedia({audio: true})) - .then(audioStream => { - var testAudio = createMediaElement('audio', 'testAudio'); - var audioPlayback = new LocalMediaStreamPlayback(testAudio, - audioStream); + audioStreamPlayback.playMedia(false, function() { + audioStream.stop(); + videoStream.stop(); + SimpleTest.finish(); + }, generateErrorCallback()); - return audioPlayback.playMedia(false) - .then(() => audioStream.stop()); - }) - .then(() => videoStream.stop()); - }) - .then(() => SimpleTest.finish(), generateErrorCallback()); + }, generateErrorCallback()); + + }, generateErrorCallback()); + + }, generateErrorCallback()); }); </script> </pre> </body> </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_peerIdentity.html +++ b/dom/media/tests/mochitest/test_getUserMedia_peerIdentity.html @@ -1,18 +1,29 @@ <!DOCTYPE HTML> <html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=942367 +--> <head> - <script type="application/javascript" src="mediaStreamPlayback.js"></script> + <meta charset="utf-8"> + <title>Test mozGetUserMedia peerIdentity Constraint</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="blacksilence.js"></script> </head> <body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=942367">Test mozGetUserMedia peerIdentity Constraint</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> <pre id="test"> <script type="application/javascript"> -createHTML({ title: "Test getUserMedia peerIdentity Constraint", bug: "942367" }); function theTest() { function testPeerIdentityConstraint(withConstraint, done) { var config = { audio: true, video: true, fake: true }; if (withConstraint) { config.peerIdentity = 'user@example.com'; } info('getting media with constraints: ' + JSON.stringify(config)); navigator.mediaDevices.getUserMedia(config).then(function(stream) {
--- a/dom/media/tests/mochitest/test_getUserMedia_playAudioTwice.html +++ b/dom/media/tests/mochitest/test_getUserMedia_playAudioTwice.html @@ -1,26 +1,46 @@ -<!DOCTYPE HTML> +<!DOCTYPE HTML> <html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=822109 +--> <head> + <meta charset="utf-8"> + <title>mozGetUserMedia Play Audio Twice</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="mediaStreamPlayback.js"></script> </head> <body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia Play Audio Twice</a> +<p id="display"></p> +<div id="content" style="display: none"> + <audio id="testAudio"></audio> +</div> <pre id="test"> <script type="application/javascript"> - createHTML({title: "getUserMedia Play Audio Twice", bug: "822109" }); /** * Run a test that we can complete an audio playback cycle twice in a row. */ runTest(function () { - getUserMedia({audio: true}).then(audioStream => { - var testAudio = createMediaElement('audio', 'testAudio'); - var playback = new LocalMediaStreamPlayback(testAudio, audioStream); + getUserMedia({audio: true}, function(audioStream) { + var testAudio = document.getElementById('testAudio'); + var audioStreamPlayback = new LocalMediaStreamPlayback(testAudio, + audioStream); + + audioStreamPlayback.playMedia(false, function() { - return playback.playMedia(false) - .then(() => playback.playMedia(true)) - .then(() => audioStream.stop()); - }).then(() => SimpleTest.finish(), generateErrorCallback()); + audioStreamPlayback.playMedia(true, function() { + audioStream.stop(); + SimpleTest.finish(); + }, generateErrorCallback()); + + }, generateErrorCallback()); + + }, generateErrorCallback()); }); + </script> </pre> </body> </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_playVideoAudioTwice.html +++ b/dom/media/tests/mochitest/test_getUserMedia_playVideoAudioTwice.html @@ -1,27 +1,45 @@ -<!DOCTYPE HTML> +<!DOCTYPE HTML> <html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=822109 +--> <head> + <meta charset="utf-8"> + <title>mozGetUserMedia Play Video and Audio Twice</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="mediaStreamPlayback.js"></script> </head> <body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia Play Video and Audio Twice</a> +<p id="display"></p> +<div id="content" style="display: none"> + <video id="testVideo"></video> +</div> <pre id="test"> <script type="application/javascript"> - createHTML({title: "getUserMedia Play Video and Audio Twice", bug: "822109" }); /** * Run a test that we can complete a video playback cycle twice in a row. */ runTest(function () { - getUserMedia({video: true, audio: true}).then(stream => { - var testVideo = createMediaElement('video', 'testVideo'); - var playback = new LocalMediaStreamPlayback(testVideo, stream); + getUserMedia({video: true, audio: true}, function(stream) { + var testVideo = document.getElementById('testVideo'); + var streamPlayback = new LocalMediaStreamPlayback(testVideo, stream); + + streamPlayback.playMedia(false, function() { - return playback.playMedia(false) - .then(() => playback.playMedia(true)) - .then(() => stream.stop()); - }).then(() => SimpleTest.finish(), generateErrorCallback()); + streamPlayback.playMedia(true, function() { + stream.stop(); + SimpleTest.finish(); + }, generateErrorCallback()); + + }, generateErrorCallback()); + + }, generateErrorCallback()); }); </script> </pre> </body> </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_playVideoTwice.html +++ b/dom/media/tests/mochitest/test_getUserMedia_playVideoTwice.html @@ -1,27 +1,46 @@ -<!DOCTYPE HTML> +<!DOCTYPE HTML> <html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=822109 +--> <head> + <meta charset="utf-8"> + <title>mozGetUserMedia Play Video Twice</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="mediaStreamPlayback.js"></script> </head> <body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia Play Video Twice</a> +<p id="display"></p> +<div id="content" style="display: none"> + <video id="testVideo"></video> +</div> <pre id="test"> <script type="application/javascript"> - createHTML({ title: "getUserMedia Play Video Twice", bug: "822109" }); /** * Run a test that we can complete a video playback cycle twice in a row. */ runTest(function () { - getUserMedia({video: true}).then(stream => { - var testVideo = createMediaElement('video', 'testVideo'); - var streamPlayback = new LocalMediaStreamPlayback(testVideo, stream); + getUserMedia({video: true}, function(videoStream) { + var testVideo = document.getElementById('testVideo'); + var videoStreamPlayback = new LocalMediaStreamPlayback(testVideo, + videoStream); + + videoStreamPlayback.playMedia(false, function() { - return streamPlayback.playMedia(false) - .then(() => streamPlayback.playMedia(true)) - .then(() => stream.stop()); - }).then(() => SimpleTest.finish(), generateErrorCallback()); + videoStreamPlayback.playMedia(true, function() { + videoStream.stop(); + SimpleTest.finish(); + }, generateErrorCallback()); + + }, generateErrorCallback()); + + }, generateErrorCallback()); }); </script> </pre> </body> </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_stopAudioStream.html +++ b/dom/media/tests/mochitest/test_getUserMedia_stopAudioStream.html @@ -1,28 +1,39 @@ -<!DOCTYPE HTML> +<!DOCTYPE HTML> <html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=822109 +--> <head> + <meta charset="utf-8"> + <title>mozGetUserMedia Stop Audio Stream</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="mediaStreamPlayback.js"></script> </head> <body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia Stop Audio Stream</a> +<p id="display"></p> +<div id="content" style="display: none"> + <audio id="testAudio"></video> +</div> <pre id="test"> <script type="application/javascript"> - createHTML({ title: "getUserMedia Stop Audio Stream", bug: "822109" }); /** * Run a test to verify that we can start an audio stream in a media element, * call stop() on the stream, and successfully get an ended event fired. */ runTest(function () { - getUserMedia({audio: true}) - .then(stream => { - var testAudio = createMediaElement('audio', 'testAudio'); - var streamPlayback = new LocalMediaStreamPlayback(testAudio, stream); + getUserMedia({audio: true}, function(stream) { + var testAudio = document.getElementById('testAudio'); + var audioStreamPlayback = new LocalMediaStreamPlayback(testAudio, stream); - return streamPlayback.playMediaWithStreamStop(false); - }) - .then(() => SimpleTest.finish(), generateErrorCallback()); + audioStreamPlayback.playMediaWithStreamStop(false, SimpleTest.finish, + generateErrorCallback()); + }, generateErrorCallback()); }); </script> </pre> </body> </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_stopAudioStreamWithFollowupAudio.html +++ b/dom/media/tests/mochitest/test_getUserMedia_stopAudioStreamWithFollowupAudio.html @@ -1,36 +1,51 @@ -<!DOCTYPE HTML> +<!DOCTYPE HTML> <html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=822109 +--> <head> + <meta charset="utf-8"> + <title>mozGetUserMedia Stop Audio Stream With Followup Audio</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="mediaStreamPlayback.js"></script> </head> <body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia Stop Audio Stream With Followup Audio</a> +<p id="display"></p> +<div id="content" style="display: none"> + <audio id="testAudio"></audio> +</div> <pre id="test"> <script type="application/javascript"> - createHTML({ title: "getUserMedia Stop Audio Stream With Followup Audio", bug: "822109" }); /** * Run a test to verify that I can complete an audio gum playback in a media * element, stop the stream, and then complete another audio gum playback * in a media element. */ runTest(function () { - getUserMedia({audio: true}) - .then(firstStream => { - var testAudio = createMediaElement('audio', 'testAudio'); - var streamPlayback = new LocalMediaStreamPlayback(testAudio, firstStream); + getUserMedia({audio: true}, function(firstStream) { + var testAudio = document.getElementById('testAudio'); + var streamPlayback = new LocalMediaStreamPlayback(testAudio, firstStream); + + streamPlayback.playMediaWithStreamStop(false, function() { + getUserMedia({audio: true}, function(secondStream) { + streamPlayback.mediaStream = secondStream; - return streamPlayback.playMediaWithStreamStop(false) - .then(() => getUserMedia({audio: true})) - .then(secondStream => { - streamPlayback.mediaStream = secondStream; + streamPlayback.playMedia(false, function() { + secondStream.stop(); + SimpleTest.finish(); + }, generateErrorCallback()); - return streamPlayback.playMedia(false) - .then(() => secondStream.stop()); - }); - }) - .then(() => SimpleTest.finish(), generateErrorCallback()); + }, generateErrorCallback()); + + }, generateErrorCallback()); + + }, generateErrorCallback()); }); </script> </pre> </body> </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_stopVideoAudioStream.html +++ b/dom/media/tests/mochitest/test_getUserMedia_stopVideoAudioStream.html @@ -1,29 +1,40 @@ <!DOCTYPE HTML> <html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=822109 +--> <head> + <meta charset="utf-8"> + <title>mozGetUserMedia Stop Video Audio Stream</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="mediaStreamPlayback.js"></script> </head> <body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia Stop Video Audio Stream</a> +<p id="display"></p> +<div id="content" style="display: none"> + <video id="testVideo"></video> +</div> <pre id="test"> <script type="application/javascript"> - createHTML({ title: "getUserMedia Stop Video Audio Stream", bug: "822109" }); /** * Run a test to verify that we can start a video+audio stream in a * media element, call stop() on the stream, and successfully get an * ended event fired. */ runTest(function () { - getUserMedia({video: true, audio: true}) - .then(stream => { - var testVideo = createMediaElement('video', 'testVideo'); - var playback = new LocalMediaStreamPlayback(testVideo, stream); + getUserMedia({video: true, audio: true}, function(stream) { + var testVideo = document.getElementById('testVideo'); + var streamPlayback = new LocalMediaStreamPlayback(testVideo, stream); - return playback.playMediaWithStreamStop(false); - }) - .then(() => SimpleTest.finish(), generateErrorCallback()); + streamPlayback.playMediaWithStreamStop(false, SimpleTest.finish, + generateErrorCallback()); + }, generateErrorCallback()); }); </script> </pre> </body> </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_stopVideoAudioStreamWithFollowupVideoAudio.html +++ b/dom/media/tests/mochitest/test_getUserMedia_stopVideoAudioStreamWithFollowupVideoAudio.html @@ -1,39 +1,51 @@ -<!DOCTYPE HTML> +<!DOCTYPE HTML> <html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=822109 +--> <head> + <meta charset="utf-8"> + <title>mozGetUserMedia Stop Video+Audio Stream With Followup Video+Audio</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="mediaStreamPlayback.js"></script> </head> <body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia Stop Video+Audio Stream With Followup Video+Audio</a> +<p id="display"></p> +<div id="content" style="display: none"> + <video id="testVideo"></video> +</div> <pre id="test"> <script type="application/javascript"> - createHTML({ - title: "getUserMedia Stop Video+Audio Stream With Followup Video+Audio", - bug: "822109" - }); /** * Run a test to verify that I can complete an video+audio gum playback in a * media element, stop the stream, and then complete another video+audio gum * playback in a media element. */ runTest(function () { - getUserMedia({video: true, audio: true}) - .then(stream => { - var testVideo = createMediaElement('video', 'testVideo'); - var streamPlayback = new LocalMediaStreamPlayback(testVideo, stream); + getUserMedia({video: true, audio: true}, function(firstStream) { + var testVideo = document.getElementById('testVideo'); + var streamPlayback = new LocalMediaStreamPlayback(testVideo, firstStream); + + streamPlayback.playMediaWithStreamStop(false, function() { + getUserMedia({video: true, audio: true}, function(secondStream) { + streamPlayback.mediaStream = secondStream; - return streamPlayback.playMediaWithStreamStop(false) - .then(() => getUserMedia({video: true, audio: true})) - .then(secondStream => { - streamPlayback.mediaStream = secondStream; + streamPlayback.playMedia(false, function() { + secondStream.stop(); + SimpleTest.finish(); + }, generateErrorCallback()); - return streamPlayback.playMedia(false) - .then(() => secondStream.stop()); - }); - }) - .then(() => SimpleTest.finish(), generateErrorCallback()); + }, generateErrorCallback()); + + }, generateErrorCallback()); + + }, generateErrorCallback()); }); </script> </pre> </body> </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_stopVideoStream.html +++ b/dom/media/tests/mochitest/test_getUserMedia_stopVideoStream.html @@ -1,29 +1,39 @@ -<!DOCTYPE HTML> +<!DOCTYPE HTML> <html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=822109 +--> <head> + <meta charset="utf-8"> + <title>mozGetUserMedia Stop Video Stream</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="mediaStreamPlayback.js"></script> </head> <body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia Stop Video Stream</a> +<p id="display"></p> +<div id="content" style="display: none"> + <video id="testVideo"></video> +</div> <pre id="test"> <script type="application/javascript"> - createHTML({ title: "getUserMedia Stop Video Stream", bug: "822109" }); /** - * Run a test to verify that we can start a video stream in a - * media element, call stop() on the stream, and successfully get an - * ended event fired. + * Run a test to verify that we can start a video stream in a media element, + * call stop() on the stream, and successfully get an ended event fired. */ runTest(function () { - getUserMedia({video: true}) - .then(stream => { - var testVideo = createMediaElement('video', 'testVideo'); - var streamPlayback = new LocalMediaStreamPlayback(testVideo, stream); + getUserMedia({video: true}, function(stream) { + var testVideo = document.getElementById('testVideo'); + var videoStreamPlayback = new LocalMediaStreamPlayback(testVideo, stream); - return streamPlayback.playMediaWithStreamStop(false); - }) - .then(() => SimpleTest.finish(), generateErrorCallback()); + videoStreamPlayback.playMediaWithStreamStop(false, SimpleTest.finish, + generateErrorCallback()); + }, generateErrorCallback()); }); </script> </pre> </body> </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_stopVideoStreamWithFollowupVideo.html +++ b/dom/media/tests/mochitest/test_getUserMedia_stopVideoStreamWithFollowupVideo.html @@ -1,36 +1,52 @@ -<!DOCTYPE HTML> +<!DOCTYPE HTML> <html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=822109 +--> <head> - <script src="mediaStreamPlayback.js"></script> + <meta charset="utf-8"> + <title>mozGetUserMedia Stop Video Stream With Followup Video</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> + <script type="application/javascript" src="mediaStreamPlayback.js"></script> </head> <body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia Stop Video Stream With Followup Video</a> +<p id="display"></p> +<div id="content" style="display: none"> + <video id="testVideo"></video> +</div> <pre id="test"> <script type="application/javascript"> - createHTML({ title: "getUserMedia Stop Video Stream With Followup Video", bug: "822109" }); /** - * Run a test to verify that I can complete an video gum playback in a - * media element, stop the stream, and then complete another video gum - * playback in a media element. + * Run a test to verify that I can complete an audio gum playback in a media + * element, stop the stream, and then complete another audio gum playback + * in a media element. */ runTest(function () { - getUserMedia({video: true}) - .then(stream => { - var testVideo = createMediaElement('video', 'testVideo'); - var streamPlayback = new LocalMediaStreamPlayback(testVideo, stream); + getUserMedia({video: true}, function(firstStream) { + var testVideo = document.getElementById('testVideo'); + var streamPlayback = new LocalMediaStreamPlayback(testVideo, + firstStream); + + streamPlayback.playMediaWithStreamStop(false, function() { + getUserMedia({video: true}, function(secondStream) { + streamPlayback.mediaStream = secondStream; - return streamPlayback.playMediaWithStreamStop(false) - .then(() => getUserMedia({video: true})) - .then(secondStream => { - streamPlayback.mediaStream = secondStream; + streamPlayback.playMedia(false, function() { + secondStream.stop(); + SimpleTest.finish(); + }, generateErrorCallback()); - return streamPlayback.playMedia(false) - .then(() => secondStream.stop()); - }); - }) - .then(() => SimpleTest.finish(), generateErrorCallback()); + }, generateErrorCallback()); + + }, generateErrorCallback()); + + }, generateErrorCallback()); }); </script> </pre> </body> </html>
--- a/dom/media/tests/mochitest/test_peerConnection_addCandidateInHaveLocalOffer.html +++ b/dom/media/tests/mochitest/test_peerConnection_addCandidateInHaveLocalOffer.html @@ -1,37 +1,45 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> + <script type="application/javascript" src="mediaStreamPlayback.js"></script> <script type="application/javascript" src="pc.js"></script> + <script type="application/javascript" src="templates.js"></script> + <script type="application/javascript" src="turnConfig.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "784519", title: "addCandidate (answer) in 'have-local-offer'" }); var test; runNetworkTest(function () { test = new PeerConnectionTest(); test.setMediaConstraints([{audio: true}], [{audio: true}]); test.chain.removeAfter("PC_LOCAL_SET_LOCAL_DESCRIPTION"); - test.chain.append([ - function PC_LOCAL_ADD_CANDIDATE(test) { - var candidate = new mozRTCIceCandidate( - {candidate:"1 1 UDP 2130706431 192.168.2.1 50005 typ host", - sdpMLineIndex: 1}); - return test.pcLocal._pc.addIceCandidate(candidate).then( - generateErrorCallback("addIceCandidate should have failed."), - err => { + test.chain.append([[ + "PC_LOCAL_ADD_CANDIDATE", + function (test) { + test.pcLocal.addIceCandidateAndFail( + new mozRTCIceCandidate( + {candidate:"1 1 UDP 2130706431 192.168.2.1 50005 typ host", + sdpMLineIndex: 1}), + function(err) { is(err.name, "InvalidStateError", "Error is InvalidStateError"); - }); - } - ]); + test.next(); + } ); + } + ]]); + test.run(); }); </script> </pre> </body> </html>
--- a/dom/media/tests/mochitest/test_peerConnection_addSecondAudioStream.html +++ b/dom/media/tests/mochitest/test_peerConnection_addSecondAudioStream.html @@ -1,59 +1,102 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> + <script type="application/javascript" src="mediaStreamPlayback.js"></script> <script type="application/javascript" src="pc.js"></script> + <script type="application/javascript" src="templates.js"></script> + <script type="application/javascript" src="turnConfig.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "1091242", title: "Renegotiation: add second audio stream" }); var test; runNetworkTest(function (options) { test = new PeerConnectionTest(options); test.chain.append([ - function PC_LOCAL_SETUP_NEGOTIATION_CALLBACK(test) { + [ + 'PC_LOCAL_SETUP_NEGOTIATION_CALLBACK', + function (test) { test.pcLocal.onNegotiationneededFired = false; - test.pcLocal._pc.onnegotiationneeded = anEvent => { + test.pcLocal._pc.onnegotiationneeded = function (anEvent) { info("pcLocal.onnegotiationneeded fired"); test.pcLocal.onNegotiationneededFired = true; }; - }, - function PC_LOCAL_ADD_SECOND_STREAM(test) { - return test.pcLocal.getAllUserMedia([{audio: true}]); - }, - function PC_LOCAL_CREATE_NEW_OFFER(test) { + test.next(); + } + ], + [ + 'PC_LOCAL_ADD_SECOND_STREAM', + function (test) { + test.pcLocal.getAllUserMedia([{audio: true}], function () { + test.next(); + }); + } + ], + [ + 'PC_LOCAL_CREATE_NEW_OFFER', + function (test) { ok(test.pcLocal.onNegotiationneededFired, "onnegotiationneeded"); - return test.createOffer(test.pcLocal).then(offer => { + test.createOffer(test.pcLocal, function (offer) { test._new_offer = offer; + test.next(); + }); + } + ], + [ + 'PC_LOCAL_SET_NEW_LOCAL_DESCRIPTION', + function (test) { + test.setLocalDescription(test.pcLocal, test._new_offer, HAVE_LOCAL_OFFER, function () { + test.next(); }); - }, - function PC_LOCAL_SET_NEW_LOCAL_DESCRIPTION(test) { - return test.setLocalDescription(test.pcLocal, test._new_offer, HAVE_LOCAL_OFFER); - }, - function PC_REMOTE_SET_NEW_REMOTE_DESCRIPTION(test) { - return test.setRemoteDescription(test.pcRemote, test._new_offer, HAVE_REMOTE_OFFER); - }, - function PC_REMOTE_CREATE_NEW_ANSWER(test) { - return test.createAnswer(test.pcRemote).then(answer => { + } + ], + [ + 'PC_REMOTE_SET_NEW_REMOTE_DESCRIPTION', + function (test) { + test.setRemoteDescription(test.pcRemote, test._new_offer, HAVE_REMOTE_OFFER, function () { + test.next(); + }); + } + ], + [ + 'PC_REMOTE_CREATE_NEW_ANSWER', + function (test) { + test.createAnswer(test.pcRemote, function (answer) { test._new_answer = answer; + test.next(); }); - }, - function PC_REMOTE_SET_NEW_LOCAL_DESCRIPTION(test) { - return test.setLocalDescription(test.pcRemote, test._new_answer, STABLE); - }, - function PC_LOCAL_SET_NEW_REMOTE_DESCRIPTION(test) { - return test.setRemoteDescription(test.pcLocal, test._new_answer, STABLE); + } + ], + [ + 'PC_REMOTE_SET_NEW_LOCAL_DESCRIPTION', + function (test) { + test.setLocalDescription(test.pcRemote, test._new_answer, STABLE, function () { + test.next(); + }); } - // TODO(bug 1093835): figure out how to verify if media flows through the new stream + ], + [ + 'PC_LOCAL_SET_NEW_REMOTE_DESCRIPTION', + function (test) { + test.setRemoteDescription(test.pcLocal, test._new_answer, STABLE, function () { + test.next(); + }); + } + ] + // TODO(bug 1093835): figure out how to verify if media flows through the new stream ]); test.setMediaConstraints([{audio: true}], [{audio: true}]); test.run(); }); </script> </pre> </body> </html>
--- a/dom/media/tests/mochitest/test_peerConnection_basicAudio.html +++ b/dom/media/tests/mochitest/test_peerConnection_basicAudio.html @@ -1,12 +1,18 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> + <script type="application/javascript" src="mediaStreamPlayback.js"></script> <script type="application/javascript" src="pc.js"></script> + <script type="application/javascript" src="templates.js"></script> + <script type="application/javascript" src="turnConfig.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "796892", title: "Basic audio-only peer connection" });
--- a/dom/media/tests/mochitest/test_peerConnection_basicAudioVideo.html +++ b/dom/media/tests/mochitest/test_peerConnection_basicAudioVideo.html @@ -1,12 +1,18 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> + <script type="application/javascript" src="mediaStreamPlayback.js"></script> <script type="application/javascript" src="pc.js"></script> + <script type="application/javascript" src="templates.js"></script> + <script type="application/javascript" src="turnConfig.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "796890", title: "Basic audio/video (separate) peer connection" });
--- a/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoCombined.html +++ b/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoCombined.html @@ -1,12 +1,18 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> + <script type="application/javascript" src="mediaStreamPlayback.js"></script> <script type="application/javascript" src="pc.js"></script> + <script type="application/javascript" src="templates.js"></script> + <script type="application/javascript" src="turnConfig.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "796890", title: "Basic audio/video (combined) peer connection" });
--- a/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoCombined_long.html +++ b/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoCombined_long.html @@ -1,37 +1,44 @@ <!DOCTYPE HTML> <!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="long.js"></script> + <script type="application/javascript" src="mediaStreamPlayback.js"></script> <script type="application/javascript" src="pc.js"></script> + <script type="application/javascript" src="templates.js"></script> + <script type="application/javascript" src="turnConfig.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "1014328", title: "Basic audio/video (combined) peer connection, long running", visible: true }); var test; - runNetworkTest(function (options) { + runTest(function (options) { options = options || {}; options.commands = commandsPeerConnection.slice(0); options.commands.push(generateIntervalCommand(verifyConnectionStatus, 1000 * 10, 1000 * 3600 * 3)); test = new PeerConnectionTest(options); test.setMediaConstraints([{audio: true, video: true, fake: false}], [{audio: true, video: true, fake: false}]); test.run(); }); </script> </pre> </body> </html> +
--- a/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoNoBundle.html +++ b/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoNoBundle.html @@ -1,34 +1,43 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> + <script type="application/javascript" src="mediaStreamPlayback.js"></script> <script type="application/javascript" src="pc.js"></script> + <script type="application/javascript" src="templates.js"></script> + <script type="application/javascript" src="turnConfig.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "1016476", title: "Basic audio/video peer connection with no Bundle" }); - runNetworkTest(options => { - var test = new PeerConnectionTest(options); - test.chain.insertAfter( - 'PC_LOCAL_CREATE_OFFER', - [ - function PC_LOCAL_REMOVE_BUNDLE_FROM_OFFER(test) { - test.originalOffer.sdp = test.originalOffer.sdp.replace( - /a=group:BUNDLE .*\r\n/g, - "" - ); - info("Updated no bundle offer: " + JSON.stringify(test.originalOffer)); - } - ]); + SimpleTest.requestFlakyTimeout("WebRTC is full of inherent timeouts"); + + var test; + runNetworkTest(function (options) { + test = new PeerConnectionTest(options); + test.chain.insertAfter('PC_LOCAL_CREATE_OFFER', + [['PC_LOCAL_REMOVE_BUNDLE_FROM_OFFER', + function (test) { + test.originalOffer.sdp = test.originalOffer.sdp.replace( + /a=group:BUNDLE .*\r\n/g, + "" + ); + info("Updated no bundle offer: " + JSON.stringify(test.originalOffer)); + test.next(); + } + ]]); test.setMediaConstraints([{audio: true}, {video: true}], [{audio: true}, {video: true}]); test.run(); }); </script> </pre> </body> </html>
--- a/dom/media/tests/mochitest/test_peerConnection_basicAudio_long.html +++ b/dom/media/tests/mochitest/test_peerConnection_basicAudio_long.html @@ -1,18 +1,24 @@ <!DOCTYPE HTML> <!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="long.js"></script> + <script type="application/javascript" src="mediaStreamPlayback.js"></script> <script type="application/javascript" src="pc.js"></script> + <script type="application/javascript" src="templates.js"></script> + <script type="application/javascript" src="turnConfig.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "796892", title: "Basic audio-only peer connection", visible: true
--- a/dom/media/tests/mochitest/test_peerConnection_basicH264Video.html +++ b/dom/media/tests/mochitest/test_peerConnection_basicH264Video.html @@ -1,12 +1,18 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> + <script type="application/javascript" src="mediaStreamPlayback.js"></script> <script type="application/javascript" src="pc.js"></script> + <script type="application/javascript" src="templates.js"></script> + <script type="application/javascript" src="turnConfig.js"></script> </head> <body> <pre id="test"> <script type="application/javascript;version=1.8"> createHTML({ bug: "1040346", title: "Basic H.264 GMP video-only peer connection" });
--- a/dom/media/tests/mochitest/test_peerConnection_basicScreenshare.html +++ b/dom/media/tests/mochitest/test_peerConnection_basicScreenshare.html @@ -1,12 +1,18 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> + <script type="application/javascript" src="mediaStreamPlayback.js"></script> <script type="application/javascript" src="pc.js"></script> + <script type="application/javascript" src="templates.js"></script> + <script type="application/javascript" src="turnConfig.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "1039666", title: "Basic screenshare-only peer connection" });
--- a/dom/media/tests/mochitest/test_peerConnection_basicVideo.html +++ b/dom/media/tests/mochitest/test_peerConnection_basicVideo.html @@ -1,12 +1,18 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> + <script type="application/javascript" src="mediaStreamPlayback.js"></script> <script type="application/javascript" src="pc.js"></script> + <script type="application/javascript" src="templates.js"></script> + <script type="application/javascript" src="turnConfig.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "796888", title: "Basic video-only peer connection" });
--- a/dom/media/tests/mochitest/test_peerConnection_basicVideo_long.html +++ b/dom/media/tests/mochitest/test_peerConnection_basicVideo_long.html @@ -1,18 +1,24 @@ <!DOCTYPE HTML> <!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="long.js"></script> + <script type="application/javascript" src="mediaStreamPlayback.js"></script> <script type="application/javascript" src="pc.js"></script> + <script type="application/javascript" src="templates.js"></script> + <script type="application/javascript" src="turnConfig.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "796888", title: "Basic video-only peer connection", visible: true
--- a/dom/media/tests/mochitest/test_peerConnection_basicWindowshare.html +++ b/dom/media/tests/mochitest/test_peerConnection_basicWindowshare.html @@ -1,12 +1,18 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> + <script type="application/javascript" src="mediaStreamPlayback.js"></script> <script type="application/javascript" src="pc.js"></script> + <script type="application/javascript" src="templates.js"></script> + <script type="application/javascript" src="turnConfig.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "1038926", title: "Basic windowshare-only peer connection" });
--- a/dom/media/tests/mochitest/test_peerConnection_bug1013809.html +++ b/dom/media/tests/mochitest/test_peerConnection_bug1013809.html @@ -1,12 +1,17 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="pc.js"></script> + <script type="application/javascript" src="templates.js"></script> + <script type="application/javascript" src="turnConfig.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "1013809", title: "Audio-only peer connection with swapped setLocal and setRemote steps" });
--- a/dom/media/tests/mochitest/test_peerConnection_bug1042791.html +++ b/dom/media/tests/mochitest/test_peerConnection_bug1042791.html @@ -1,12 +1,18 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> + <script type="application/javascript" src="mediaStreamPlayback.js"></script> <script type="application/javascript" src="pc.js"></script> + <script type="application/javascript" src="templates.js"></script> + <script type="application/javascript" src="turnConfig.js"></script> </head> <body> <pre id="test"> <script type="application/javascript;version=1.8"> createHTML({ bug: "1040346", title: "Basic H.264 GMP video-only peer connection" }); @@ -14,23 +20,25 @@ var test; runNetworkTest(function (options) { options = options || { }; options.h264 = true; test = new PeerConnectionTest(options); test.setMediaConstraints([{video: true}], [{video: true}]); test.chain.removeAfter("PC_LOCAL_CREATE_OFFER"); - test.chain.append([ - function PC_LOCAL_VERIFY_H264_OFFER(test) { + test.chain.append([[ + "PC_LOCAL_VERIFY_H264_OFFER", + function (test) { ok(!test.pcLocal._latest_offer.sdp.toLowerCase().contains("profile-level-id=0x42e0"), - "H264 offer does not contain profile-level-id=0x42e0"); + "H264 offer does not contain profile-level-id=0x42e0"); ok(test.pcLocal._latest_offer.sdp.toLowerCase().contains("profile-level-id=42e0"), - "H264 offer contains profile-level-id=42e0"); + "H264 offer contains profile-level-id=42e0"); + test.next(); } - ]); + ]]); test.run(); }); </script> </pre> </body> </html>
--- a/dom/media/tests/mochitest/test_peerConnection_bug822674.html +++ b/dom/media/tests/mochitest/test_peerConnection_bug822674.html @@ -1,11 +1,14 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="pc.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "822674", title: "mozRTCPeerConnection isn't a true javascript object as it should be"
--- a/dom/media/tests/mochitest/test_peerConnection_bug825703.html +++ b/dom/media/tests/mochitest/test_peerConnection_bug825703.html @@ -1,80 +1,83 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="pc.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "825703", title: "RTCConfiguration valid/invalid permutations" }); -var makePC = (config, expected_error) => { - var exception; - try { - new mozRTCPeerConnection(config).close(); - } catch (e) { - exception = e; - } - is((exception? exception.name : "success"), expected_error || "success", - "mozRTCPeerConnection(" + JSON.stringify(config) + ")"); -}; - -// This is a test of the iceServers parsing code + readable errors -runNetworkTest(() => { - var exception = null; - - try { - new mozRTCPeerConnection().close(); - } catch (e) { - exception = e; - } - ok(!exception, "mozRTCPeerConnection() succeeds"); - exception = null; - - makePC(); - - makePC(1, "TypeError"); - - makePC({}); - - makePC({ iceServers: [] }); - - makePC({ iceServers: [{ urls:"" }] }, "SyntaxError"); - - makePC({ iceServers: [ - { urls:"stun:127.0.0.1" }, - { urls:"stun:localhost", foo:"" }, - { urls: ["stun:127.0.0.1", "stun:localhost"] }, - { urls:"stuns:localhost", foo:"" }, - { urls:"turn:[::1]:3478", username:"p", credential:"p" }, - { urls:"turn:localhost:3478?transport=udp", username:"p", credential:"p" }, - { urls: ["turn:[::1]:3478", "turn:localhost"], username:"p", credential:"p" }, - { urls:"turns:localhost:3478?transport=udp", username:"p", credential:"p" }, - { url:"stun:localhost", foo:"" }, - { url:"turn:localhost", username:"p", credential:"p" } - ]}); - - makePC({ iceServers: [{ urls: ["stun:127.0.0.1", ""] }] }, "SyntaxError"); - - makePC({ iceServers: [{ urls:"turns:localhost:3478", username:"p" }] }, "InvalidAccessError"); - - makePC({ iceServers: [{ url:"turns:localhost:3478", credential:"p" }] }, "InvalidAccessError"); - - makePC({ iceServers: [{ urls:"http:0.0.0.0" }] }, "SyntaxError"); - - try { - new mozRTCPeerConnection({ iceServers: [{ url:"http:0.0.0.0" }] }).close(); - } catch (e) { - ok(e.message.indexOf("http") > 0, - "mozRTCPeerConnection() constructor has readable exceptions"); + makePC = (config, expected_error) => { + var exception; + try { + new mozRTCPeerConnection(config).close(); + } catch (e) { + exception = e; + } + is((exception? exception.name : "success"), expected_error || "success", + "mozRTCPeerConnection(" + JSON.stringify(config) + ")"); } - networkTestFinished(); -}); + // This is a test of the iceServers parsing code + readable errors + + runNetworkTest(function () { + var exception = null; + + try { + new mozRTCPeerConnection().close(); + } catch (e) { + exception = e; + } + ok(!exception, "mozRTCPeerConnection() succeeds"); + exception = null; + + makePC(); + + makePC(1, "TypeError"); + + makePC({}); + + makePC({ iceServers: [] }); + + makePC({ iceServers: [{ urls:"" }] }, "SyntaxError"); + + makePC({ iceServers: [ + { urls:"stun:127.0.0.1" }, + { urls:"stun:localhost", foo:"" }, + { urls: ["stun:127.0.0.1", "stun:localhost"] }, + { urls:"stuns:localhost", foo:"" }, + { urls:"turn:[::1]:3478", username:"p", credential:"p" }, + { urls:"turn:localhost:3478?transport=udp", username:"p", credential:"p" }, + { urls: ["turn:[::1]:3478", "turn:localhost"], username:"p", credential:"p" }, + { urls:"turns:localhost:3478?transport=udp", username:"p", credential:"p" }, + { url:"stun:localhost", foo:"" }, + { url:"turn:localhost", username:"p", credential:"p" } + ]}); + + makePC({ iceServers: [{ urls: ["stun:127.0.0.1", ""] }] }, "SyntaxError"); + + makePC({ iceServers: [{ urls:"turns:localhost:3478", username:"p" }] }, "InvalidAccessError"); + + makePC({ iceServers: [{ url:"turns:localhost:3478", credential:"p" }] }, "InvalidAccessError"); + + makePC({ iceServers: [{ urls:"http:0.0.0.0" }] }, "SyntaxError"); + try { + new mozRTCPeerConnection({ iceServers: [{ url:"http:0.0.0.0" }] }).close(); + } catch (e) { + ok(e.message.indexOf("http") > 0, + "mozRTCPeerConnection() constructor has readable exceptions"); + } + + networkTestFinished(); + }); </script> </pre> </body> </html>
--- a/dom/media/tests/mochitest/test_peerConnection_bug827843.html +++ b/dom/media/tests/mochitest/test_peerConnection_bug827843.html @@ -1,70 +1,72 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="pc.js"></script> + <script type="application/javascript" src="templates.js"></script> + <script type="application/javascript" src="turnConfig.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "827843", title: "Ensure that localDescription and remoteDescription are null after close" }); -var steps = [ - function CHECK_SDP_ON_CLOSED_PC(test) { - var description; - var exception = null; + var steps = [ + [ + "CHECK_SDP_ON_CLOSED_PC", + function (test) { + var description; + var exception = null; - // handle the event which the close() triggers - var localClosed = new Promise(resolve => { - test.pcLocal.onsignalingstatechange = e => { - is(e.target.signalingState, "closed", - "Received expected onsignalingstatechange event on 'closed'"); - resolve(); - } - }); + // handle the event which the close() triggers + test.pcLocal.onsignalingstatechange = function (e) { + is(e.target.signalingState, "closed", + "Received expected onsignalingstatechange event on 'closed'"); + } + + test.pcLocal.close(); - test.pcLocal.close(); + try { description = test.pcLocal.localDescription; } catch (e) { exception = e; } + ok(exception, "Attempt to access localDescription of pcLocal after close throws exception"); + exception = null; - try { description = test.pcLocal.localDescription; } catch (e) { exception = e; } - ok(exception, "Attempt to access localDescription of pcLocal after close throws exception"); - exception = null; - - try { description = test.pcLocal.remoteDescription; } catch (e) { exception = e; } - ok(exception, "Attempt to access remoteDescription of pcLocal after close throws exception"); - exception = null; + try { description = test.pcLocal.remoteDescription; } catch (e) { exception = e; } + ok(exception, "Attempt to access remoteDescription of pcLocal after close throws exception"); + exception = null; - // handle the event which the close() triggers - var remoteClosed = new Promise(resolve => { - test.pcRemote.onsignalingstatechange = e => { - is(e.target.signalingState, "closed", - "Received expected onsignalingstatechange event on 'closed'"); - resolve(); - } - }); + // handle the event which the close() triggers + test.pcRemote.onsignalingstatechange = function (e) { + is(e.target.signalingState, "closed", + "Received expected onsignalingstatechange event on 'closed'"); + } - test.pcRemote.close(); + test.pcRemote.close(); - try { description = test.pcRemote.localDescription; } catch (e) { exception = e; } - ok(exception, "Attempt to access localDescription of pcRemote after close throws exception"); - exception = null; + try { description = test.pcRemote.localDescription; } catch (e) { exception = e; } + ok(exception, "Attempt to access localDescription of pcRemote after close throws exception"); + exception = null; - try { description = test.pcRemote.remoteDescription; } catch (e) { exception = e; } - ok(exception, "Attempt to access remoteDescription of pcRemote after close throws exception"); + try { description = test.pcRemote.remoteDescription; } catch (e) { exception = e; } + ok(exception, "Attempt to access remoteDescription of pcRemote after close throws exception"); - return Promise.all([localClosed, remoteClosed]); - } -]; + test.next(); + } + ] + ]; -var test; -runNetworkTest(() => { - test = new PeerConnectionTest(); - test.setMediaConstraints([{audio: true}], [{audio: true}]); - test.chain.append(steps); - test.run(); -}); + var test; + runNetworkTest(function () { + test = new PeerConnectionTest(); + test.setMediaConstraints([{audio: true}], [{audio: true}]); + test.chain.append(steps); + test.run(); + }); </script> </pre> </body> </html>
--- a/dom/media/tests/mochitest/test_peerConnection_bug834153.html +++ b/dom/media/tests/mochitest/test_peerConnection_bug834153.html @@ -1,11 +1,14 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="pc.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "834153", title: "Queue CreateAnswer in PeerConnection.js"
deleted file mode 100644 --- a/dom/media/tests/mochitest/test_peerConnection_callbacks.html +++ /dev/null @@ -1,92 +0,0 @@ -<!DOCTYPE HTML> -<html> -<head> - <script type="application/javascript" src="pc.js"></script> -</head> -<body> -<pre id="test"> -<script type="application/javascript;version=1.8"> - createHTML({ - title: "PeerConnection using callback functions", - bug: "1119593", - visible: true - }); - -// This still aggressively uses promises, but it is testing that the callback functions -// are properly in place. - -// wrapper that turns a callback-based function call into a promise -function pcall(o, f, beforeArg) { - return new Promise((resolve, reject) => { - var args = [resolve, reject]; - if (typeof beforeArg !== 'undefined') { - args.unshift(beforeArg); - } - info('Calling ' + f.name); - f.apply(o, args); - }); -} - -var pc1 = new mozRTCPeerConnection(); -var pc2 = new mozRTCPeerConnection(); - -var pc2_haveRemoteOffer = new Promise(resolve => { - pc2.onsignalingstatechange = - e => (e.target.signalingState == "have-remote-offer") && resolve(); -}); -var pc1_stable = new Promise(resolve => { - pc1.onsignalingstatechange = - e => (e.target.signalingState == "stable") && resolve(); -}); - -pc1.onicecandidate = e => { - pc2_haveRemoteOffer - .then(() => !e.candidate || pcall(pc2, pc2.addIceCandidate, e.candidate)) - .catch(generateErrorCallback()); -}; -pc2.onicecandidate = e => { - pc1_stable - .then(() => !e.candidate || pcall(pc1, pc1.addIceCandidate, e.candidate)) - .catch(generateErrorCallback()); -}; - -var v1, v2; -var delivered = new Promise(resolve => { - pc2.onaddstream = e => { - v2.mozSrcObject = e.stream; - resolve(e.stream); - }; -}); - -runNetworkTest(function() { - v1 = createMediaElement('video', 'v1'); - v2 = createMediaElement('video', 'v2'); - var canPlayThrough = new Promise(resolve => v2.canplaythrough = resolve); - is(v2.currentTime, 0, "v2.currentTime is zero at outset"); - - // not testing legacy gUM here - navigator.mediaDevices.getUserMedia({ fake: true, video: true, audio: true }) - .then(stream => pc1.addStream(v1.mozSrcObject = stream)) - .then(() => pcall(pc1, pc1.createOffer)) - .then(offer => pcall(pc1, pc1.setLocalDescription, offer)) - .then(() => pcall(pc2, pc2.setRemoteDescription, pc1.localDescription)) - .then(() => pcall(pc2, pc2.createAnswer)) - .then(answer => pcall(pc2, pc2.setLocalDescription, answer)) - .then(() => pcall(pc1, pc1.setRemoteDescription, pc2.localDescription)) - .then(() => delivered) - // .then(() => canPlayThrough) // why doesn't this fire? - .then(() => waitUntil(() => v2.currentTime > 0 && v2.mozSrcObject.currentTime > 0)) - .then(() => ok(v2.currentTime > 0, "v2.currentTime is moving (" + v2.currentTime + ")")) - .then(() => ok(true, "Connected.")) - .then(() => pcall(pc1, pc1.getStats, null)) - .then(stats => ok(Object.keys(stats).length > 0, "pc1 has stats")) - .then(() => pcall(pc2, pc2.getStats, null)) - .then(stats => ok(Object.keys(stats).length > 0, "pc2 has stats")) - .then(() => { v1.pause(); v2.pause(); }) - .catch(reason => ok(false, "unexpected failure: " + reason)) - .then(networkTestFinished); -}); -</script> -</pre> -</body> -</html>
--- a/dom/media/tests/mochitest/test_peerConnection_capturedVideo.html +++ b/dom/media/tests/mochitest/test_peerConnection_capturedVideo.html @@ -1,45 +1,55 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> + <script type="application/javascript" src="mediaStreamPlayback.js"></script> <script type="application/javascript" src="pc.js"></script> + <script type="application/javascript" src="templates.js"></script> + <script type="application/javascript" src="turnConfig.js"></script> </head> <body> <video id="v1" src="../../test/vp9cake.webm" height="120" width="160" autoplay muted></video> <pre id="test"> <script type="application/javascript;version=1.8"> createHTML({ bug: "1081409", title: "Captured video-only over peer connection", visible: true }); -var metadataLoaded = new Promise(resolve => { - if (v1.readyState < v1.HAVE_METADATA) { - v1.onloadedmetadata = resolve; - } else { + var metadataLoaded = new Promise(resolve => { + if (v1.readyState < v1.HAVE_METADATA) { + v1.onloadedmetadata = e => resolve(); + return; + } resolve(); - } -}); + }); -runNetworkTest(function() { - var test = new PeerConnectionTest(); - test.setOfferOptions({ offerToReceiveVideo: false, - offerToReceiveAudio: false }); - test.chain.insertAfter("PC_LOCAL_GUM", [ - function PC_LOCAL_CAPTUREVIDEO(test) { - return metadataLoaded - .then(() => { - var stream = v1.mozCaptureStreamUntilEnded(); - is(stream.getTracks().length, 2, "Captured stream has 2 tracks"); - stream.getTracks().forEach(tr => test.pcLocal._pc.addTrack(tr, stream)); - test.pcLocal.constraints = [{ video: true, audio:true }]; // fool tests - }); + runNetworkTest(function() { + var test = new PeerConnectionTest(); + test.setOfferOptions({ offerToReceiveVideo: false, + offerToReceiveAudio: false }); + test.chain.insertAfter("PC_LOCAL_GUM", [["PC_LOCAL_CAPTUREVIDEO", function (test) { + metadataLoaded + .then(function() { + var stream = v1.mozCaptureStreamUntilEnded(); + is(stream.getTracks().length, 2, "Captured stream has 2 tracks"); + stream.getTracks().forEach(tr => test.pcLocal._pc.addTrack(tr, stream)); + test.pcLocal.constraints = [{ video: true, audio:true }]; // fool tests + test.next(); + }) + .catch(function(reason) { + ok(false, "unexpected failure: " + reason); + SimpleTest.finish(); + }); } - ]); - test.chain.removeAfter("PC_REMOTE_CHECK_MEDIA_FLOW_PRESENT"); - test.run(); -}); + ]]); + test.chain.removeAfter("PC_REMOTE_CHECK_MEDIA_FLOW_PRESENT"); + test.run(); + }); </script> </pre> </body> </html>
--- a/dom/media/tests/mochitest/test_peerConnection_close.html +++ b/dom/media/tests/mochitest/test_peerConnection_close.html @@ -1,11 +1,14 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="pc.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "991877", title: "Basic RTCPeerConnection.close() tests"
--- a/dom/media/tests/mochitest/test_peerConnection_errorCallbacks.html +++ b/dom/media/tests/mochitest/test_peerConnection_errorCallbacks.html @@ -1,11 +1,14 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="pc.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "834270", title: "Align PeerConnection error handling with WebRTC specification"
--- a/dom/media/tests/mochitest/test_peerConnection_noTrickleAnswer.html +++ b/dom/media/tests/mochitest/test_peerConnection_noTrickleAnswer.html @@ -1,13 +1,19 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> + <script type="application/javascript" src="mediaStreamPlayback.js"></script> <script type="application/javascript" src="nonTrickleIce.js"></script> <script type="application/javascript" src="pc.js"></script> + <script type="application/javascript" src="templates.js"></script> + <script type="application/javascript" src="turnConfig.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "1060102", title: "Basic audio only SDP answer without trickle ICE" });
--- a/dom/media/tests/mochitest/test_peerConnection_noTrickleOffer.html +++ b/dom/media/tests/mochitest/test_peerConnection_noTrickleOffer.html @@ -1,13 +1,19 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> + <script type="application/javascript" src="mediaStreamPlayback.js"></script> <script type="application/javascript" src="nonTrickleIce.js"></script> <script type="application/javascript" src="pc.js"></script> + <script type="application/javascript" src="templates.js"></script> + <script type="application/javascript" src="turnConfig.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "1060102", title: "Basic audio only SDP offer without trickle ICE" });
--- a/dom/media/tests/mochitest/test_peerConnection_noTrickleOfferAnswer.html +++ b/dom/media/tests/mochitest/test_peerConnection_noTrickleOfferAnswer.html @@ -1,13 +1,19 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> + <script type="application/javascript" src="mediaStreamPlayback.js"></script> <script type="application/javascript" src="nonTrickleIce.js"></script> <script type="application/javascript" src="pc.js"></script> + <script type="application/javascript" src="templates.js"></script> + <script type="application/javascript" src="turnConfig.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "1060102", title: "Basic audio only SDP offer and answer without trickle ICE" });
--- a/dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveAudio.html +++ b/dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveAudio.html @@ -1,12 +1,17 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="pc.js"></script> + <script type="application/javascript" src="templates.js"></script> + <script type="application/javascript" src="turnConfig.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "850275", title: "Simple offer media constraint test with audio" });
--- a/dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveVideo.html +++ b/dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveVideo.html @@ -1,12 +1,17 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="pc.js"></script> + <script type="application/javascript" src="templates.js"></script> + <script type="application/javascript" src="turnConfig.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "850275", title: "Simple offer media constraint test with video" });
--- a/dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveVideoAudio.html +++ b/dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveVideoAudio.html @@ -1,12 +1,17 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="pc.js"></script> + <script type="application/javascript" src="templates.js"></script> + <script type="application/javascript" src="turnConfig.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "850275", title: "Simple offer media constraint test with video/audio" });
--- a/dom/media/tests/mochitest/test_peerConnection_promiseSendOnly.html +++ b/dom/media/tests/mochitest/test_peerConnection_promiseSendOnly.html @@ -1,57 +1,62 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="pc.js"></script> </head> <body> +<video id="v1" controls="controls" height="120" width="160" autoplay></video> +<video id="v2" controls="controls" height="120" width="160" autoplay></video><br> <pre id="test"> <script type="application/javascript;version=1.8"> createHTML({ bug: "1091898", title: "PeerConnection with promises (sendonly)", visible: true }); + var waituntil = func => new Promise(resolve => { + var inter = setInterval(() => func() && resolve(clearInterval(inter)), 200); + }); + var pc1 = new mozRTCPeerConnection(); var pc2 = new mozRTCPeerConnection(); var pc2_haveRemoteOffer = new Promise(resolve => pc2.onsignalingstatechange = e => (e.target.signalingState == "have-remote-offer") && resolve()); var pc1_stable = new Promise(resolve => pc1.onsignalingstatechange = e => (e.target.signalingState == "stable") && resolve()); pc1.onicecandidate = e => pc2_haveRemoteOffer.then(() => !e.candidate || pc2.addIceCandidate(e.candidate)).catch(generateErrorCallback()); pc2.onicecandidate = e => pc1_stable.then(() => !e.candidate || pc1.addIceCandidate(e.candidate)).catch(generateErrorCallback()); - var v1, v2; var delivered = new Promise(resolve => pc2.onaddstream = e => resolve(v2.mozSrcObject = e.stream)); + var canPlayThrough = new Promise(resolve => v2.canplaythrough = e => resolve()); runNetworkTest(function() { - v1 = createMediaElement('video', 'v1'); - v2 = createMediaElement('video', 'v2'); - var canPlayThrough = new Promise(resolve => v2.canplaythrough = e => resolve()); - is(v2.currentTime, 0, "v2.currentTime is zero at outset"); navigator.mediaDevices.getUserMedia({ fake: true, video: true, audio: true }) .then(stream => pc1.addStream(v1.mozSrcObject = stream)) .then(() => pc1.createOffer()) .then(offer => pc1.setLocalDescription(offer)) .then(() => pc2.setRemoteDescription(pc1.localDescription)) .then(() => pc2.createAnswer()) .then(answer => pc2.setLocalDescription(answer)) .then(() => pc1.setRemoteDescription(pc2.localDescription)) .then(() => delivered) // .then(() => canPlayThrough) // why doesn't this fire? - .then(() => waitUntil(() => v2.currentTime > 0 && v2.mozSrcObject.currentTime > 0)) + .then(() => waituntil(() => v2.currentTime > 0 && v2.mozSrcObject.currentTime > 0)) .then(() => ok(v2.currentTime > 0, "v2.currentTime is moving (" + v2.currentTime + ")")) .then(() => ok(true, "Connected.")) .catch(reason => ok(false, "unexpected failure: " + reason)) .then(networkTestFinished); }); </script> </pre> </body>
--- a/dom/media/tests/mochitest/test_peerConnection_replaceTrack.html +++ b/dom/media/tests/mochitest/test_peerConnection_replaceTrack.html @@ -1,12 +1,18 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> + <script type="application/javascript" src="mediaStreamPlayback.js"></script> <script type="application/javascript" src="pc.js"></script> + <script type="application/javascript" src="templates.js"></script> + <script type="application/javascript" src="turnConfig.js"></script> </head> <body> <pre id="test"> <script type="application/javascript;version=1.8"> createHTML({ bug: "1032839", title: "Replace video track", visible: true @@ -20,33 +26,37 @@ var test; runNetworkTest(function () { test = new PeerConnectionTest(); test.setMediaConstraints([{video: true}], [{video: true}]); test.chain.removeAfter("PC_REMOTE_CHECK_MEDIA_FLOW_PRESENT"); var flowtest = test.chain.remove("PC_REMOTE_CHECK_MEDIA_FLOW_PRESENT"); test.chain.append(flowtest); - test.chain.append([ - function PC_LOCAL_REPLACE_VIDEOTRACK(test) { + test.chain.append([["PC_LOCAL_REPLACE_VIDEOTRACK", + function (test) { var stream = test.pcLocal._pc.getLocalStreams()[0]; var track = stream.getVideoTracks()[0]; var sender = test.pcLocal._pc.getSenders().find(isSenderOfTrack, track); ok(sender, "track has a sender"); var newtrack; - return navigator.mediaDevices.getUserMedia({video:true, fake: true}) - .then(newStream => { - newtrack = newStream.getVideoTracks()[0]; - return sender.replaceTrack(newtrack); - }) - .then(() => { - is(sender.track, newtrack, "sender.track has been replaced"); - }); + navigator.mediaDevices.getUserMedia({video:true, fake: true}) + .then(function(newStream) { + newtrack = newStream.getVideoTracks()[0]; + return sender.replaceTrack(newtrack); + }) + .then(function() { + is(sender.track, newtrack, "sender.track has been replaced"); + }) + .catch(function(reason) { + ok(false, "unexpected error = " + reason.message); + }) + .then(test.next.bind(test)); } - ]); + ]]); test.chain.append(flowtest); test.run(); }); </script> </pre> </body> </html>
--- a/dom/media/tests/mochitest/test_peerConnection_setLocalAnswerInHaveLocalOffer.html +++ b/dom/media/tests/mochitest/test_peerConnection_setLocalAnswerInHaveLocalOffer.html @@ -1,34 +1,43 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> + <script type="application/javascript" src="mediaStreamPlayback.js"></script> <script type="application/javascript" src="pc.js"></script> + <script type="application/javascript" src="templates.js"></script> + <script type="application/javascript" src="turnConfig.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "784519", title: "setLocalDescription (answer) in 'have-local-offer'" }); -runNetworkTest(function () { - var test = new PeerConnectionTest(); - test.setMediaConstraints([{audio: true}], [{audio: true}]); - test.chain.removeAfter("PC_LOCAL_SET_LOCAL_DESCRIPTION"); + var test; + runNetworkTest(function () { + test = new PeerConnectionTest(); + test.setMediaConstraints([{audio: true}], [{audio: true}]); + test.chain.removeAfter("PC_LOCAL_SET_LOCAL_DESCRIPTION"); - test.chain.append([ - function PC_LOCAL_SET_LOCAL_ANSWER(test) { - test.pcLocal._latest_offer.type = "answer"; - return test.pcLocal.setLocalDescriptionAndFail(test.pcLocal._latest_offer) - .then(err => { - is(err.name, "InvalidStateError", "Error is InvalidStateError"); - }); - } - ]); + test.chain.append([[ + "PC_LOCAL_SET_LOCAL_ANSWER", + function (test) { + test.pcLocal._latest_offer.type="answer"; + test.pcLocal.setLocalDescriptionAndFail(test.pcLocal._latest_offer, + function(err) { + is(err.name, "InvalidStateError", "Error is InvalidStateError"); + test.next(); + } ); + } + ]]); - test.run(); -}); + test.run(); + }); </script> </pre> </body> </html>
--- a/dom/media/tests/mochitest/test_peerConnection_setLocalAnswerInStable.html +++ b/dom/media/tests/mochitest/test_peerConnection_setLocalAnswerInStable.html @@ -1,34 +1,43 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> + <script type="application/javascript" src="mediaStreamPlayback.js"></script> <script type="application/javascript" src="pc.js"></script> + <script type="application/javascript" src="templates.js"></script> + <script type="application/javascript" src="turnConfig.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "784519", title: "setLocalDescription (answer) in 'stable'" }); -runNetworkTest(function () { - var test = new PeerConnectionTest(); - test.setMediaConstraints([{audio: true}], [{audio: true}]); - test.chain.removeAfter("PC_LOCAL_CREATE_OFFER"); + var test; + runNetworkTest(function () { + test = new PeerConnectionTest(); + test.setMediaConstraints([{audio: true}], [{audio: true}]); + test.chain.removeAfter("PC_LOCAL_CREATE_OFFER"); - test.chain.append([ - function PC_LOCAL_SET_LOCAL_ANSWER(test) { - test.pcLocal._latest_offer.type = "answer"; - return test.pcLocal.setLocalDescriptionAndFail(test.pcLocal._latest_offer) - .then(err => { - is(err.name, "InvalidStateError", "Error is InvalidStateError"); - }); - } - ]); + test.chain.append([[ + "PC_LOCAL_SET_LOCAL_ANSWER", + function (test) { + test.pcLocal._latest_offer.type="answer"; + test.pcLocal.setLocalDescriptionAndFail(test.pcLocal._latest_offer, + function(err) { + is(err.name, "InvalidStateError", "Error is InvalidStateError"); + test.next(); + } ); + } + ]]); - test.run(); -}); + test.run(); + }); </script> </pre> </body> </html>
--- a/dom/media/tests/mochitest/test_peerConnection_setLocalOfferInHaveRemoteOffer.html +++ b/dom/media/tests/mochitest/test_peerConnection_setLocalOfferInHaveRemoteOffer.html @@ -1,33 +1,42 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> + <script type="application/javascript" src="mediaStreamPlayback.js"></script> <script type="application/javascript" src="pc.js"></script> + <script type="application/javascript" src="templates.js"></script> + <script type="application/javascript" src="turnConfig.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "784519", title: "setLocalDescription (offer) in 'have-remote-offer'" }); -runNetworkTest(function () { - var test = new PeerConnectionTest(); - test.setMediaConstraints([{audio: true}], [{audio: true}]); - test.chain.removeAfter("PC_REMOTE_SET_REMOTE_DESCRIPTION"); + var test; + runNetworkTest(function () { + test = new PeerConnectionTest(); + test.setMediaConstraints([{audio: true}], [{audio: true}]); + test.chain.removeAfter("PC_REMOTE_SET_REMOTE_DESCRIPTION"); - test.chain.append([ - function PC_REMOTE_SET_LOCAL_OFFER(test) { - test.pcRemote.setLocalDescriptionAndFail(test.pcLocal._latest_offer) - .then(err => { - is(err.name, "InvalidStateError", "Error is InvalidStateError"); - }); - } - ]); + test.chain.append([[ + "PC_REMOTE_SET_LOCAL_OFFER", + function (test) { + test.pcRemote.setLocalDescriptionAndFail(test.pcLocal._latest_offer, + function(err) { + is(err.name, "InvalidStateError", "Error is InvalidStateError"); + test.next(); + } ); + } + ]]); - test.run(); -}); + test.run(); + }); </script> </pre> </body> </html>
--- a/dom/media/tests/mochitest/test_peerConnection_setRemoteAnswerInHaveRemoteOffer.html +++ b/dom/media/tests/mochitest/test_peerConnection_setRemoteAnswerInHaveRemoteOffer.html @@ -1,34 +1,43 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> + <script type="application/javascript" src="mediaStreamPlayback.js"></script> <script type="application/javascript" src="pc.js"></script> + <script type="application/javascript" src="templates.js"></script> + <script type="application/javascript" src="turnConfig.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "784519", title: "setRemoteDescription (answer) in 'have-remote-offer'" }); -runNetworkTest(function () { - var test = new PeerConnectionTest(); - test.setMediaConstraints([{audio: true}], [{audio: true}]); - test.chain.removeAfter("PC_REMOTE_SET_REMOTE_DESCRIPTION"); + var test; + runNetworkTest(function () { + test = new PeerConnectionTest(); + test.setMediaConstraints([{audio: true}], [{audio: true}]); + test.chain.removeAfter("PC_REMOTE_SET_REMOTE_DESCRIPTION"); - test.chain.append([ - function PC_REMOTE_SET_REMOTE_ANSWER(test) { - test.pcLocal._latest_offer.type = "answer"; - test.pcRemote._pc.setRemoteDescription(test.pcLocal._latest_offer) - .then(generateErrorCallback('setRemoteDescription should fail'), - err => - is(err.name, "InvalidStateError", "Error is InvalidStateError")); - } - ]); + test.chain.append([[ + "PC_REMOTE_SET_REMOTE_ANSWER", + function (test) { + test.pcLocal._latest_offer.type="answer"; + test.pcRemote.setRemoteDescriptionAndFail(test.pcLocal._latest_offer, + function(err) { + is(err.name, "InvalidStateError", "Error is InvalidStateError"); + test.next(); + } ); + } + ]]); - test.run(); -}); + test.run(); + }); </script> </pre> </body> </html>
--- a/dom/media/tests/mochitest/test_peerConnection_setRemoteAnswerInStable.html +++ b/dom/media/tests/mochitest/test_peerConnection_setRemoteAnswerInStable.html @@ -1,34 +1,43 @@ <!DOCTYPE HTML> <html> <head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> + <script type="application/javascript" src="mediaStreamPlayback.js"></script> <script type="application/javascript" src="pc.js"></script> + <script type="application/javascript" src="templates.js"></script> + <script type="application/javascript" src="turnConfig.js"></script> </head> <body> <pre id="test"> <script type="application/javascript"> createHTML({ bug: "784519", title: "setRemoteDescription (answer) in 'stable'" }); -runNetworkTest(function () { - var test = new PeerConnectionTest(); - test.setMediaConstraints([{audio: true}], [{audio: true}]); - test.chain.removeAfter("PC_LOCAL_CREATE_OFFER"); + var test; + runNetworkTest(function () { + test = new PeerConnectionTest(); + test.setMediaConstraints([{audio: true}], [{audio: true}]); + test.chain.removeAfter("PC_LOCAL_CREATE_OFFER"); - test.chain.append([ - function PC_LOCAL_SET_REMOTE_ANSWER(test) { - test.pcLocal.