Update web-platform-tests to revision d7afcb8708eac08a614d161d5622a48172daf7e3

This commit is contained in:
WPT Sync Bot 2019-05-15 10:40:54 -04:00 committed by Josh Matthews
parent 6f8bb4dd40
commit edff458e23
791 changed files with 17647 additions and 10322 deletions

View file

@ -78,13 +78,24 @@ function createDtmfSender(pc = new RTCPeerConnection()) {
Test description.
*/
function test_tone_change_events(testFunc, toneChanges, desc) {
async_test(t => {
// Convert to cumulative time
let cumulativeTime = 0;
const cumulativeToneChanges = toneChanges.map(c => {
cumulativeTime += c[2];
return [c[0], c[1], cumulativeTime];
});
// Wait for same duration as last expected duration + 100ms
// before passing test in case there are new tone events fired,
// in which case the test should fail.
const lastWait = toneChanges.pop()[2] + 100;
promise_test(async t => {
const pc = new RTCPeerConnection();
const dtmfSender = await createDtmfSender(pc);
const start = Date.now();
createDtmfSender(pc)
.then(dtmfSender => {
let lastEventTime = Date.now();
const allEventsReceived = new Promise(resolve => {
const onToneChange = t.step_func(ev => {
assert_true(ev instanceof RTCDTMFToneChangeEvent,
'Expect tone change event object to be an RTCDTMFToneChangeEvent');
@ -93,12 +104,12 @@ function test_tone_change_events(testFunc, toneChanges, desc) {
assert_equals(typeof tone, 'string',
'Expect event.tone to be the tone string');
assert_greater_than(toneChanges.length, 0,
assert_greater_than(cumulativeToneChanges.length, 0,
'More tonechange event is fired than expected');
const [
expectedTone, expectedToneBuffer, expectedDuration
] = toneChanges.shift();
expectedTone, expectedToneBuffer, expectedTime
] = cumulativeToneChanges.shift();
assert_equals(tone, expectedTone,
`Expect current event.tone to be ${expectedTone}`);
@ -106,37 +117,24 @@ function test_tone_change_events(testFunc, toneChanges, desc) {
assert_equals(dtmfSender.toneBuffer, expectedToneBuffer,
`Expect dtmfSender.toneBuffer to be updated to ${expectedToneBuffer}`);
const now = Date.now();
const duration = now - lastEventTime;
// We check that the delay is at least the expected one, but
// system load may cause random delay, so we do not put any
// realistic upper bound on the timing of the event.
assert_between_inclusive(duration, expectedDuration,
expectedDuration + 4000,
`Expect tonechange event for "${tone}" to be fired approximately after ${expectedDuration} milliseconds`);
lastEventTime = now;
if (toneChanges.length === 0) {
// Wait for same duration as last expected duration + 100ms
// before passing test in case there are new tone events fired,
// in which case the test should fail.
t.step_timeout(
t.step_func(() => {
t.done();
pc.close();
pc.otherPc.close();
}), expectedDuration + 100);
// We check that the cumulative delay is at least the expected one, but
// system load may cause random delays, so we do not put any
// realistic upper bound on the timing of the events.
assert_between_inclusive(Date.now() - start, expectedTime,
expectedTime + 4000,
`Expect tonechange event for "${tone}" to be fired approximately after ${expectedTime} milliseconds`);
if (cumulativeToneChanges.length === 0) {
resolve();
}
});
dtmfSender.addEventListener('tonechange', onToneChange);
testFunc(t, dtmfSender, pc);
})
.catch(t.step_func(err => {
assert_unreached(`Unexpected promise rejection: ${err}`);
}));
});
testFunc(t, dtmfSender, pc);
await allEventsReceived;
const wait = ms => new Promise(resolve => t.step_timeout(resolve, ms));
await wait(lastWait);
}, desc);
}

View file

@ -87,7 +87,7 @@
if(dtlsTransport.state === 'connected') {
onConnected(dtlsTransport);
} else {
assert_array_equals(dtlsTransport.getCertificates(), [],
assert_array_equals(dtlsTransport.getRemoteCertificates(), [],
'Expect DTLS certificates be initially empty until become connected');
dtlsTransport.addEventListener('statechange', t.step_func(() => {

View file

@ -8,7 +8,7 @@
'use strict';
// The following helper functions are called from RTCPeerConnection-helper.js:
// exchangeIceCandidates
// coupleIceCandidates
// doSignalingHandshake
// trackFactories.audio()
@ -42,36 +42,6 @@ function resolveWhen(t, dtlstransport, state) {
});
}
// Helper class to exchange ice candidates between
// two local peer connections
class CandidateChannel {
constructor(source, dest) {
source.addEventListener('icecandidate', event => {
const { candidate } = event;
if (candidate && this.activated
&& this.destination.signalingState !== 'closed') {
this.destination.addIceCandidate(candidate);
} else {
this.queue.push(candidate);
}
});
this.destination = dest;
this.activated = false;
this.queue = [];
}
activate() {
this.activated = true;
for (const candidate of this.queue) {
this.destination.addIceCandidate(candidate);
}
}
}
function coupleCandidates(pc1, pc2) {
const ch1 = new CandidateChannel(pc1, pc2);
const ch2 = new CandidateChannel(pc2, pc1);
return [ch1, ch2];
}
async function setupConnections(t) {
const pc1 = new RTCPeerConnection();
@ -80,11 +50,8 @@ async function setupConnections(t) {
t.add_cleanup(() => pc2.close());
pc1.addTrack(trackFactories.audio());
const channels = coupleCandidates(pc1, pc2);
const channels = coupleIceCandidates(pc1, pc2);
await doSignalingHandshake(pc1, pc2);
for (const channel of channels) {
channel.activate();
}
return [pc1, pc2];
}

View file

@ -181,6 +181,46 @@ function exchangeIceCandidates(pc1, pc2) {
doExchange(pc2, pc1);
}
// Helper class to exchange ice candidates between
// two local peer connections
class CandidateChannel {
constructor(source, dest, name) {
source.addEventListener('icecandidate', event => {
const { candidate } = event;
if (candidate && this.activated
&& this.destination.signalingState !== 'closed') {
this.destination.addIceCandidate(candidate);
} else if (candidate) {
this.queue.push(candidate);
}
});
dest.addEventListener('signalingstatechange', event => {
if (this.destination.signalingState == 'stable' && !this.activated) {
this.activate();
}
});
this.name = name;
this.destination = dest;
this.activated = false;
this.queue = [];
}
activate() {
this.activated = true;
for (const candidate of this.queue) {
this.destination.addIceCandidate(candidate);
}
}
}
// Alternate function to exchange ICE candidates between two
// PeerConnections. Unlike exchangeIceCandidates, it will function
// correctly if candidates are added before descriptions are set.
function coupleIceCandidates(pc1, pc2) {
const ch1 = new CandidateChannel(pc1, pc2, 'forward');
const ch2 = new CandidateChannel(pc2, pc1, 'back');
return [ch1, ch2];
}
// Helper function for doing one round of offer/answer exchange
// between two local peer connections
async function doSignalingHandshake(localPc, remotePc, options={}) {
@ -200,9 +240,9 @@ async function doSignalingHandshake(localPc, remotePc, options={}) {
answer = await options.modifyAnswer(answer);
}
// Apply answer
await remotePc.setLocalDescription(answer);
// Apply answer. Note: localPc should enter stable state first.
await localPc.setRemoteDescription(answer);
await remotePc.setLocalDescription(answer);
}
// Returns a promise that resolves when |pc.iceConnectionState| is 'connected'

View file

@ -11,7 +11,7 @@
// https://w3c.github.io/webrtc-pc/archives/20170605/webrtc.html
// The following helper functions are called from RTCPeerConnection-helper.js:
// exchangeIceCandidates
// coupleIceCandidates
// doSignalingHandshake
/*
@ -132,7 +132,7 @@
pc1.addEventListener('iceconnectionstatechange', onIceConnectionStateChange);
exchangeIceCandidates(pc1, pc2);
coupleIceCandidates(pc1, pc2);
doSignalingHandshake(pc1, pc2);
}, 'connection with one data channel should eventually have connected or ' +
'completed connection state');
@ -178,11 +178,43 @@ async_test(t => {
pc1.addEventListener('iceconnectionstatechange', onIceConnectionStateChange);
exchangeIceCandidates(pc1, pc2);
coupleIceCandidates(pc1, pc2);
doSignalingHandshake(pc1, pc2);
}, 'connection with one data channel should eventually ' +
'have connected connection state');
promise_test(async t => {
const pc1 = new RTCPeerConnection();
t.add_cleanup(() => pc1.close());
const pc2 = new RTCPeerConnection();
t.add_cleanup(() => pc2.close());
const stream = await getNoiseStream({audio: true});
t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
stream.getTracks().forEach(track => pc1.addTrack(track, stream));
coupleIceCandidates(pc1, pc2);
doSignalingHandshake(pc1, pc2);
await listenToIceConnected(pc1);
}, 'connection with audio track should eventually ' +
'have connected connection state');
promise_test(async t => {
const pc1 = new RTCPeerConnection();
t.add_cleanup(() => pc1.close());
const pc2 = new RTCPeerConnection();
t.add_cleanup(() => pc2.close());
const stream = await getNoiseStream({audio: true, video:true});
t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
stream.getTracks().forEach(track => pc1.addTrack(track, stream));
coupleIceCandidates(pc1, pc2);
doSignalingHandshake(pc1, pc2);
await listenToIceConnected(pc1);
}, 'connection with audio and video tracks should eventually ' +
'have connected connection state');
promise_test(async t => {
const caller = new RTCPeerConnection();
t.add_cleanup(() => caller.close());
@ -191,9 +223,10 @@ async_test(t => {
caller.addTransceiver('audio', {direction:'recvonly'});
const stream = await navigator.mediaDevices.getUserMedia({audio:true});
t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
const [track] = stream.getTracks();
callee.addTrack(track, stream);
exchangeIceCandidates(caller, callee);
coupleIceCandidates(caller, callee);
await doSignalingHandshake(caller, callee);
assert_equals(caller.getTransceivers().length, 1);

View file

@ -100,8 +100,28 @@ promise_test(async t => {
await unmutePromise;
const mutePromise = muteWatcher.wait_for('mute');
pc2.close();
localTransceiver.stop();
await mutePromise;
}, 'pc.close() mutes remote tracks');
}, 'transceiver.stop() on one side (without renegotiation) causes mute events on the other');
promise_test(async t => {
const pc1 = createPeerConnectionWithCleanup(t);
const pc1Sender = pc1.addTrack(...await createTrackAndStreamWithCleanup(t));
const localTransceiver = findTransceiverForSender(pc1, pc1Sender);
const pc2 = createPeerConnectionWithCleanup(t);
exchangeIceCandidates(pc1, pc2);
const e = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
// Need to wait for the initial unmute event before closing, otherwise
// there will be no transition from unmuted->muted.
const muteWatcher = new EventWatcher(t, e.track, ['mute', 'unmute']);
const unmutePromise = muteWatcher.wait_for('unmute');
await exchangeAnswer(pc1, pc2);
await unmutePromise;
const mutePromise = muteWatcher.wait_for('mute');
pc1.close();
await mutePromise;
}, 'pc.close() on one side causes mute events on the other');
</script>

View file

@ -86,5 +86,67 @@
assert_equals(caller_transceiver1.receiver.transport,
caller_transceiver2.receiver.transport);
}, 'RTCRtpSender/receiver.transport at the right time, with bundle policy ' + bundle_policy);
// Do the same test again, with DataChannel in the mix.
promise_test(async t => {
const caller = new RTCPeerConnection({bundlePolicy: bundle_policy});
t.add_cleanup(() => caller.close());
const stream = await navigator.mediaDevices.getUserMedia(
{audio: true, video:true});
t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
const [track1, track2] = stream.getTracks();
const sender1 = caller.addTrack(track1);
const sender2 = caller.addTrack(track2);
caller.createDataChannel('datachannel');
const callee = new RTCPeerConnection();
t.add_cleanup(() => callee.close());
exchangeIceCandidates(caller, callee);
const offer = await caller.createOffer();
assert_equals(sender1.transport, null);
assert_equals(sender2.transport, null);
if (caller.sctp) {
assert_equals(caller.sctp.transport, null);
}
await caller.setLocalDescription(offer);
assert_not_equals(sender1.transport, null);
assert_not_equals(sender2.transport, null);
assert_not_equals(caller.sctp.transport, null);
const [caller_transceiver1, caller_transceiver2] = caller.getTransceivers();
assert_equals(sender1.transport, caller_transceiver1.sender.transport);
if (bundle_policy == 'max-bundle') {
assert_equals(caller_transceiver1.sender.transport,
caller_transceiver2.sender.transport);
assert_equals(caller_transceiver1.sender.transport,
caller.sctp.transport);
} else {
assert_not_equals(caller_transceiver1.sender.transport,
caller_transceiver2.sender.transport);
assert_not_equals(caller_transceiver1.sender.transport,
caller.sctp.transport);
}
await callee.setRemoteDescription(offer);
const [callee_transceiver1, callee_transceiver2] = callee.getTransceivers();
// According to spec, setRemoteDescription only updates the transports
// if the remote description is an answer.
assert_equals(callee_transceiver1.receiver.transport, null);
assert_equals(callee_transceiver2.receiver.transport, null);
const answer = await callee.createAnswer();
await callee.setLocalDescription(answer);
assert_not_equals(callee_transceiver1.receiver.transport, null);
assert_not_equals(callee_transceiver2.receiver.transport, null);
assert_not_equals(callee.sctp.transport, null);
// At this point, bundle should have kicked in.
assert_equals(callee_transceiver1.receiver.transport,
callee_transceiver2.receiver.transport);
assert_equals(callee_transceiver1.receiver.transport,
callee.sctp.transport,
'Callee SCTP transport does not match:');
await caller.setRemoteDescription(answer);
assert_equals(caller_transceiver1.receiver.transport,
caller_transceiver2.receiver.transport);
assert_equals(caller_transceiver1.receiver.transport,
caller.sctp.transport,
'Caller SCTP transport does not match:');
}, 'RTCRtpSender/receiver/SCTP transport at the right time, with bundle policy ' + bundle_policy);
}
</script>

View file

@ -26,7 +26,7 @@
RTCRtpTransceiver on which the method is called. Additionally, the
RTCRtpCodecParameters dictionary members cannot be modified. If
codecs does not fulfill these requirements, the user agent MUST throw
an InvalidAccessError.
an InvalidModificationError.
*/
test(() => {
@ -81,9 +81,23 @@
const pc = new RTCPeerConnection();
const transceiver = pc.addTransceiver('audio');
const capabilities = RTCRtpSender.getCapabilities('video');
assert_throws(() => transceiver.setCodecPreferences(capabilities.codecs));
assert_throws('InvalidModificationError', () => transceiver.setCodecPreferences(capabilities.codecs));
}, `setCodecPreferences() on audio transceiver with codecs returned from getCapabilities('video') should throw InvalidAccessError`);
}, `setCodecPreferences() on audio transceiver with codecs returned from getCapabilities('video') should throw InvalidModificationError`);
test(() => {
const pc = new RTCPeerConnection();
const transceiver = pc.addTransceiver('audio');
const codecs = [{
mimeType: 'data',
clockRate: 2000,
channels: 2,
sdpFmtpLine: '0-15'
}];
assert_throws('InvalidModificationError', () => transceiver.setCodecPreferences(codecs));
}, `setCodecPreferences() with user defined codec with invalid mimeType should throw InvalidModificationError`);
test(() => {
const pc = new RTCPeerConnection();
@ -92,12 +106,12 @@
mimeType: 'audio/piepiper',
clockRate: 2000,
channels: 2,
sdpFmtpLine: 'a=fmtp:98 0-15'
sdpFmtpLine: '0-15'
}];
assert_throws(() => transceiver.setCodecPreferences(codecs));
assert_throws('InvalidModificationError', () => transceiver.setCodecPreferences(codecs));
}, `setCodecPreferences() with user defined codec should throw InvalidAccessError`);
}, `setCodecPreferences() with user defined codec should throw InvalidModificationError`);
test(() => {
const pc = new RTCPeerConnection();
@ -109,12 +123,45 @@
mimeType: 'audio/piepiper',
clockRate: 2000,
channels: 2,
sdpFmtpLine: 'a=fmtp:98 0-15'
sdpFmtpLine: '0-15'
}];
assert_throws(() => transceiver.setCodecPreferences(codecs));
assert_throws('InvalidModificationError', () => transceiver.setCodecPreferences(codecs));
}, `setCodecPreferences() with user defined codec together with codecs returned from getCapabilities() should throw InvalidAccessError`);
}, `setCodecPreferences() with user defined codec together with codecs returned from getCapabilities() should throw InvalidModificationError`);
test(() => {
const pc = new RTCPeerConnection();
const transceiver = pc.addTransceiver('audio');
const capabilities = RTCRtpSender.getCapabilities('audio');
const codecs = [capabilities.codecs[0]];
codecs[0].clockRate = codecs[0].clockRate / 2;
assert_throws('InvalidModificationError', () => transceiver.setCodecPreferences(codecs));
}, `setCodecPreferences() with modified codec clock rate should throw InvalidModificationError`);
test(() => {
const pc = new RTCPeerConnection();
const transceiver = pc.addTransceiver('audio');
const capabilities = RTCRtpSender.getCapabilities('audio');
const codecs = [capabilities.codecs[0]];
codecs[0].channels = codecs[0].channels + 11;
assert_throws('InvalidModificationError', () => transceiver.setCodecPreferences(codecs));
}, `setCodecPreferences() with modified codec channel count should throw InvalidModificationError`);
test(() => {
const pc = new RTCPeerConnection();
const transceiver = pc.addTransceiver('audio');
const capabilities = RTCRtpSender.getCapabilities('audio');
const codecs = [capabilities.codecs[0]];
codecs[0].sdpFmtpLine = "modifiedparameter=1";
assert_throws('InvalidModificationError', () => transceiver.setCodecPreferences(codecs));
}, `setCodecPreferences() with modified codec parameters should throw InvalidModificationError`);
test(() => {
const pc = new RTCPeerConnection();
@ -129,8 +176,8 @@
const { channels=2 } = codec;
codec.channels = channels+1;
assert_throws(() => transceiver.setCodecPreferences(codecs));
assert_throws('InvalidModificationError', () => transceiver.setCodecPreferences(codecs));
}, `setCodecPreferences() with modified codecs returned from getCapabilities() should throw InvalidAccessError`);
}, `setCodecPreferences() with modified codecs returned from getCapabilities() should throw InvalidModificationError`);
</script>

View file

@ -1972,6 +1972,30 @@
]);
};
const checkBundleTagRejected = async t => {
const pc1 = new RTCPeerConnection();
t.add_cleanup(() => pc1.close());
const pc2 = new RTCPeerConnection();
t.add_cleanup(() => pc2.close());
const stream1 = await getNoiseStream({audio: true});
t.add_cleanup(() => stopTracks(stream1));
const track1 = stream1.getAudioTracks()[0];
const stream2 = await getNoiseStream({audio: true});
t.add_cleanup(() => stopTracks(stream2));
const track2 = stream2.getAudioTracks()[0];
pc1.addTrack(track1, stream1);
pc1.addTrack(track2, stream2);
await offerAnswer(pc1, pc2);
pc2.getTransceivers()[0].stop();
await offerAnswer(pc1, pc2);
await offerAnswer(pc2, pc1);
};
const checkMsectionReuse = async t => {
// Use max-compat to make it easier to check for disabled m-sections
const pc1 = new RTCPeerConnection({ bundlePolicy: "max-compat" });
@ -2254,7 +2278,9 @@ const tests = [
checkRollbackAndSetRemoteOfferWithDifferentType,
checkRemoteRollback,
checkMsectionReuse,
checkStopAfterCreateOfferWithReusedMsection
checkStopAfterCreateOfferWithReusedMsection,
checkAddIceCandidateToStoppedTransceiver,
checkBundleTagRejected
].forEach(test => promise_test(test, test.name));
</script>

View file

@ -36,6 +36,23 @@ a=ssrc:3 cname:4
a=ssrc:3 msid:1 2
`;
const sdp2 = sdpBase + `
a=ssrc:3 cname:4
a=ssrc:3 msid:1 2
`;
const sdp3 = sdpBase + `
a=msid:1 2
a=ssrc:3 cname:4
a=ssrc:3 msid:3 2
`;
const sdp4 = sdp1.replace('msid-semantic', 'unknownattr');
const sdp5 = sdpBase + `
a=msid:-
`;
async function applyRemoteDescriptionAndReturnRemoteTrackAndStreams(pc, sdp)
{
const testTrackPromise = new Promise(resolve => {
@ -45,6 +62,64 @@ async function applyRemoteDescriptionAndReturnRemoteTrackAndStreams(pc, sdp)
return testTrackPromise;
}
promise_test(async test => {
const pc = new RTCPeerConnection();
test.add_cleanup(() => pc.close());
const [track, streams] = await applyRemoteDescriptionAndReturnRemoteTrackAndStreams(pc, sdp0);
assert_equals(streams.length, 1, "track event has a stream");
}, "When a=msid is absent, the track should still be associated with a stream");
promise_test(async test => {
const pc = new RTCPeerConnection();
test.add_cleanup(() => pc.close());
const [track, streams] = await applyRemoteDescriptionAndReturnRemoteTrackAndStreams(pc, sdp1);
assert_equals(streams.length, 1, "track event has a stream");
assert_equals(streams[0].id, "1", "msid should match");
}, "Source-level msid should be ignored if media-level msid is present");
promise_test(async test => {
const pc = new RTCPeerConnection();
test.add_cleanup(() => pc.close());
const [track, streams] = await applyRemoteDescriptionAndReturnRemoteTrackAndStreams(pc, sdp2);
assert_equals(streams.length, 1, "track event has a stream");
assert_equals(streams[0].id, "1", "msid should match");
}, "Source-level msid should be parsed if media-level msid is absent");
promise_test(async test => {
const pc = new RTCPeerConnection();
test.add_cleanup(() => pc.close());
let track;
let streams;
try {
[track, streams] = await applyRemoteDescriptionAndReturnRemoteTrackAndStreams(pc, sdp3);
} catch (e) {
return;
}
assert_equals(streams.length, 1, "track event has a stream");
assert_equals(streams[0].id, "1", "msid should match");
}, "Source-level msid should be ignored, or an error should be thrown, if a different media-level msid is present");
promise_test(async test => {
const pc = new RTCPeerConnection();
test.add_cleanup(() => pc.close());
const [track, streams] = await applyRemoteDescriptionAndReturnRemoteTrackAndStreams(pc, sdp4);
assert_equals(streams.length, 1, "track event has a stream");
assert_equals(streams[0].id, "1", "msid should match");
}, "stream ids should be found even if msid-semantic is absent");
promise_test(async test => {
const pc = new RTCPeerConnection();
test.add_cleanup(() => pc.close());
const [track, streams] = await applyRemoteDescriptionAndReturnRemoteTrackAndStreams(pc, sdp5);
assert_equals(streams.length, 0, "track event has no stream");
}, "a=msid:- should result in a track event with no streams");
promise_test(async test => {
const pc = new RTCPeerConnection();
test.add_cleanup(() => pc.close());

View file

@ -36,9 +36,9 @@ a=rtpmap:96 VP8/90000
a=rtcp-fb:96 goog-remb
a=rtcp-fb:96 transport-cc
a=rtcp-fb:96 ccm fir
a=rid:foo send
a=rid:bar send
a=rid:baz send
a=rid:foo recv
a=rid:bar recv
a=rid:baz recv
a=simulcast:recv foo;bar;baz
`;