https://github.com/web-platform-tests/wpt
Tip revision: a99112edef5480c5a92159a8bf772af8ef00fc85 authored by Lennart Grahl on 24 May 2018, 22:01:25 UTC
fixup! fixup! Add an ugly workaround for createDataChannelPair to make it work in Safari
fixup! fixup! Add an ugly workaround for createDataChannelPair to make it work in Safari
Tip revision: a99112e
RTCPeerConnection-setRemoteDescription-tracks.https.html
<!doctype html>
<meta charset=utf-8>
<title>RTCPeerConnection.prototype.setRemoteDescription - add/remove remote tracks</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="RTCPeerConnection-helper.js"></script>
<script>
'use strict';
// Test is based on the following editor draft:
// https://w3c.github.io/webrtc-pc/archives/20171002/webrtc.html
// The following helper functions are called from RTCPeerConnection-helper.js:
// getUserMediaTracksAndStreams
// performOffer
// Resolver
// These tests are concerned with the observable consequences of processing
// the addition or removal of remote tracks, including events firing and the
// states of RTCPeerConnection, MediaStream and MediaStreamTrack.
async_test(t => {
const caller = new RTCPeerConnection();
const callee = new RTCPeerConnection();
return getUserMediaTracksAndStreams(1)
.then(t.step_func(([tracks, streams]) => {
const localTrack = tracks[0];
caller.addTrack(localTrack);
const offerPromise = performOffer(caller, callee);
callee.ontrack = t.step_func(trackEvent => {
const remoteTrack = trackEvent.track;
assert_equals(remoteTrack.id, localTrack.id,
'Expected local and remote track IDs to match.');
assert_equals(trackEvent.streams.length, 0,
'Expected remote track not to belong to a stream.');
t.done();
});
return offerPromise;
}))
.catch(t.step_func(reason => {
assert_unreached(reason);
}));
}, 'addTrack() with a track and no stream makes ontrack fire with a track and no stream.');
async_test(t => {
const caller = new RTCPeerConnection();
const callee = new RTCPeerConnection();
return getUserMediaTracksAndStreams(1)
.then(t.step_func(([tracks, streams]) => {
const localTrack = tracks[0];
const localStream = streams[0];
caller.addTrack(localTrack, localStream);
const offerPromise = performOffer(caller, callee);
callee.ontrack = t.step_func(trackEvent => {
assert_equals(trackEvent.streams.length, 1,
'Expected track event to fire with a single stream.');
const remoteTrack = trackEvent.track;
const remoteStream = trackEvent.streams[0];
assert_equals(remoteTrack.id, localTrack.id,
'Expected local and remote track IDs to match.');
assert_equals(remoteStream.id, localStream.id,
'Expected local and remote stream IDs to match.');
assert_array_equals(remoteStream.getTracks(), [remoteTrack],
'Expected the remote stream\'s tracks to be the remote track.');
t.done();
});
return offerPromise;
}))
.catch(t.step_func(reason => {
assert_unreached(reason);
}));
}, 'addTrack() with a track and a stream makes ontrack fire with a track and a stream.');
async_test(t => {
const caller = new RTCPeerConnection();
const callee = new RTCPeerConnection();
let eventSequence = '';
return getUserMediaTracksAndStreams(1)
.then(t.step_func(([tracks, streams]) => {
const ontrackResolver = new Resolver();
callee.ontrack = () => {
eventSequence += 'ontrack;';
ontrackResolver.resolve();
}
caller.addTrack(tracks[0]);
return Promise.all([
ontrackResolver.promise,
performOffer(caller, callee).then(() => {
eventSequence += 'setRemoteDescription;';
})
]);
}))
.then(t.step_func(() => {
assert_equals(eventSequence, 'ontrack;setRemoteDescription;');
t.done();
}))
.catch(t.step_func(reason => {
assert_unreached(reason);
}));
}, 'ontrack fires before setRemoteDescription resolves.');
async_test(t => {
const caller = new RTCPeerConnection();
const callee = new RTCPeerConnection();
return getUserMediaTracksAndStreams(2)
.then(t.step_func(([tracks, streams]) => {
const localTrack1 = tracks[0];
const localTrack2 = tracks[1];
const localStream = streams[0];
caller.addTrack(localTrack1, localStream);
caller.addTrack(localTrack2, localStream);
const offerPromise = performOffer(caller, callee);
callee.ontrack = t.step_func(trackEvent => {
assert_equals(trackEvent.streams.length, 1,
'Expected track event to fire with a single stream.');
const remoteTrack1 = trackEvent.track;
const remoteStream = trackEvent.streams[0];
assert_equals(remoteTrack1.id, localTrack1.id,
'Expected first remote track ID to match first local track ID.');
assert_equals(remoteStream.getTracks().length, 2,
'Expected the remote stream to contain two tracks.');
callee.ontrack = t.step_func(trackEvent => {
assert_equals(trackEvent.streams.length, 1,
'Expected track event to fire with a single stream.');
const remoteTrack2 = trackEvent.track;
assert_equals(trackEvent.streams[0], remoteStream,
'Expected both track events to fire with the same remote stream.');
assert_equals(remoteTrack2.id, localTrack2.id,
'Expected second remote track ID to match second local track ID.');
assert_equals(remoteStream.getTracks().length, 2,
'Expected the remote stream to contain two tracks.');
assert_array_equals(remoteStream.getTracks(), [remoteTrack1, remoteTrack2],
'Expected the remote stream\'s tracks to be the [first, second] remote tracks.');
t.done();
});
});
return offerPromise;
}))
.catch(t.step_func(reason => {
assert_unreached(reason);
}));
}, 'addTrack() with two tracks and one stream makes ontrack fire twice with the tracks and shared stream.');
async_test(t => {
const caller = new RTCPeerConnection();
const callee = new RTCPeerConnection();
return getUserMediaTracksAndStreams(2)
.then(t.step_func(([tracks, streams]) => {
const localTracks = tracks;
const localStream = streams[0];
caller.addTrack(localTracks[0], localStream);
const offerPromise = performOffer(caller, callee);
callee.ontrack = t.step_func(trackEvent => {
assert_equals(trackEvent.streams.length, 1,
'Expected track event to fire with a single stream.');
const remoteTracks = [ trackEvent.track ];
const remoteStream = trackEvent.streams[0];
assert_equals(remoteTracks[0].id, localTracks[0].id, 'Remote track ID.');
assert_equals(remoteStream.id, localStream.id, 'Remote stream ID.');
assert_array_equals(remoteStream.getTracks(), remoteTracks,
'Expected the remote stream\'s tracks to be the remote track.');
caller.addTrack(localTracks[1], localStream);
performOffer(caller, callee);
remoteStream.onaddtrack = t.step_func(trackEvent => {
remoteTracks.push(trackEvent.track);
assert_equals(remoteTracks[1].id, localTracks[1].id, 'Remote track ID.');
assert_array_equals(remoteStream.getTracks(), remoteTracks,
'Expected the remote stream\'s tracks to be the remote tracks.');
t.done();
});
});
return offerPromise;
}))
.catch(t.step_func(reason => {
assert_unreached(reason);
}));
}, 'addTrack() for an existing stream makes stream.onaddtrack fire.');
async_test(t => {
const caller = new RTCPeerConnection();
const callee = new RTCPeerConnection();
let eventSequence = '';
return getUserMediaTracksAndStreams(2)
.then(t.step_func(([tracks, streams]) => {
const localTracks = tracks;
const localStream = streams[0];
caller.addTrack(localTracks[0], localStream);
const offerPromise = performOffer(caller, callee);
callee.ontrack = t.step_func(trackEvent => {
callee.ontrack = null;
const remoteStream = trackEvent.streams[0];
const onaddtrackResolver = new Resolver();
remoteStream.onaddtrack = () => {
eventSequence += 'stream.onaddtrack;';
onaddtrackResolver.resolve();
}
caller.addTrack(localTracks[1], localStream);
Promise.all([
onaddtrackResolver.promise,
performOffer(caller, callee).then(() => {
eventSequence += 'setRemoteDescription;';
})
]).then(t.step_func(() => {
assert_equals(eventSequence, 'stream.onaddtrack;setRemoteDescription;');
t.done();
}));
});
return offerPromise;
}))
.catch(t.step_func(reason => {
assert_unreached(reason);
}));
}, 'stream.onaddtrack fires before setRemoteDescription resolves.');
async_test(t => {
const caller = new RTCPeerConnection();
const callee = new RTCPeerConnection();
return getUserMediaTracksAndStreams(2)
.then(t.step_func(([tracks, streams]) => {
const localTrack = tracks[0];
const localStreams = streams;
caller.addTrack(localTrack, localStreams[0], localStreams[1]);
const performOffer = performOffer(caller, callee);
callee.ontrack = t.step_func(trackEvent => {
assert_equals(trackEvent.streams.length, 2,
'Expected the track event to fire with two streams.');
const remoteTrack = trackEvent.track;
const remoteStreams = trackEvent.streams;
assert_equals(remoteTrack.id, localTrack.id,
'Expected local and remote track IDs to match.');
assert_equals(remoteStreams[0].id, localStreams[0].id,
'Expected the first remote stream ID to match the first local stream ID.');
assert_equals(remoteStreams[1].id, localStreams[1].id,
'Expected the second remote stream ID to match the second local stream ID.');
assert_array_equals(remoteStreams[0].getTracks(), [remoteTrack],
'Expected the remote stream\'s tracks to be the remote track.');
assert_array_equals(remoteStreams[1].getTracks(), [remoteTrack],
'Expected the remote stream\'s tracks to be the remote track.');
t.done();
});
return performOffer;
}))
.catch(t.step_func(reason => {
assert_unreached(reason);
}));
}, 'addTrack() with a track and two streams makes ontrack fire with a track and two streams.');
async_test(t => {
const caller = new RTCPeerConnection();
const callee = new RTCPeerConnection();
return getUserMediaTracksAndStreams(1)
.then(t.step_func(([tracks, streams]) => {
caller.addTrack(tracks[0]);
const offerPromise = performOffer(caller, callee);
callee.ontrack = t.step_func(trackEvent => {
assert_array_equals(callee.getReceivers(), [trackEvent.receiver]);
t.done();
});
return offerPromise;
}))
.catch(t.step_func(reason => {
assert_unreached(reason);
}));
}, 'ontrack\'s receiver matches getReceivers().');
async_test(t => {
const caller = new RTCPeerConnection();
const callee = new RTCPeerConnection();
return getUserMediaTracksAndStreams(1)
.then(t.step_func(([tracks, streams]) => {
const sender = caller.addTrack(tracks[0]);
assert_not_equals(sender, null);
const offerPromise = performOffer(caller, callee);
callee.ontrack = t.step_func(trackEvent => {
const receivers = callee.getReceivers();
assert_equals(receivers.length, 1,
'Expected getReceivers() to be the track event\'s receiver.');
caller.removeTrack(sender);
performOffer(caller, callee)
.then(t.step_func(() => {
assert_array_equals(callee.getReceivers(), receivers,
'Expected the set of receivers to remain the same.');
t.done();
}));
});
return offerPromise;
}))
.catch(t.step_func(reason => {
assert_unreached(reason);
}));
}, 'removeTrack() does not remove the receiver.');
async_test(t => {
const caller = new RTCPeerConnection();
const callee = new RTCPeerConnection();
return getUserMediaTracksAndStreams(1)
.then(t.step_func(([tracks, streams]) => {
const sender = caller.addTrack(tracks[0], streams[0]);
assert_not_equals(sender, null);
const offerPromise = performOffer(caller, callee);
callee.ontrack = t.step_func(trackEvent => {
assert_not_equals(trackEvent.track, null);
assert_equals(trackEvent.streams.length, 1);
assert_true(trackEvent.streams[0].getTracks().includes(trackEvent.track));
caller.removeTrack(sender);
performOffer(caller, callee);
trackEvent.streams[0].onremovetrack = t.step_func(removeEvent => {
assert_equals(removeEvent.track, trackEvent.track);
assert_false(trackEvent.streams[0].getTracks().includes(trackEvent.track));
t.done();
});
});
return offerPromise;
}))
.catch(t.step_func(reason => {
assert_unreached(reason);
}));
}, 'removeTrack() makes stream.onremovetrack fire and the track to be removed from the stream.');
async_test(t => {
const caller = new RTCPeerConnection();
const callee = new RTCPeerConnection();
let eventSequence = '';
return getUserMediaTracksAndStreams(1)
.then(t.step_func(([tracks, streams]) => {
const sender = caller.addTrack(tracks[0], streams[0]);
assert_not_equals(sender, null);
const offerPromise = performOffer(caller, callee);
callee.ontrack = t.step_func(trackEvent => {
const remoteStream = trackEvent.streams[0];
const onremovetrackResolver = new Resolver();
remoteStream.onremovetrack = t.step_func(removeEvent => {
eventSequence += 'stream.onremovetrack;';
onremovetrackResolver.resolve();
});
caller.removeTrack(sender);
return Promise.all([
onremovetrackResolver.promise,
performOffer(caller, callee).then(() => {
eventSequence += 'setRemoteDescription;';
})
]).then(t.step_func(() => {
assert_equals(eventSequence, 'stream.onremovetrack;setRemoteDescription;');
t.done();
}));
});
return offerPromise;
}))
.catch(t.step_func(reason => {
assert_unreached(reason);
}));
}, 'stream.onremovetrack fires before setRemoteDescription resolves.');
async_test(t => {
const caller = new RTCPeerConnection();
const callee = new RTCPeerConnection();
return getUserMediaTracksAndStreams(1)
.then(t.step_func(([tracks, streams]) => {
const sender = caller.addTrack(tracks[0]);
assert_not_equals(sender, null);
const offerPromise = performOffer(caller, callee);
callee.ontrack = t.step_func(trackEvent => {
const remoteTrack = trackEvent.track;
caller.removeTrack(sender);
performOffer(caller, callee);
remoteTrack.onmute = t.step_func(() => {
assert_true(trackEvent.track.muted);
t.done();
});
});
return offerPromise;
}))
.catch(t.step_func(reason => {
assert_unreached(reason);
}));
}, 'removeTrack() makes track.onmute fire and the track to be muted.');
async_test(t => {
const caller = new RTCPeerConnection();
const callee = new RTCPeerConnection();
let eventSequence = '';
return getUserMediaTracksAndStreams(1)
.then(t.step_func(([tracks, streams]) => {
const sender = caller.addTrack(tracks[0]);
assert_not_equals(sender, null);
const offerPromise = performOffer(caller, callee);
callee.ontrack = t.step_func(trackEvent => {
const remoteTrack = trackEvent.track;
const onmuteResolver = new Resolver();
remoteTrack.onmute = t.step_func(() => {
eventSequence += 'track.onmute;';
onmuteResolver.resolve();
});
caller.removeTrack(sender);
return Promise.all([
onmuteResolver.promise,
performOffer(caller, callee).then(() => {
eventSequence += 'setRemoteDescription;';
})
]).then(t.step_func(() => {
assert_equals(eventSequence, 'track.onmute;setRemoteDescription;');
t.done();
}));
});
return offerPromise;
}))
.catch(t.step_func(reason => {
assert_unreached(reason);
}));
}, 'track.onmute fires before setRemoteDescription resolves.');
async_test(t => {
const pc = new RTCPeerConnection();
return getUserMediaTracksAndStreams(1)
.then(t.step_func(([tracks, streams]) => {
const sender = pc.addTrack(tracks[0]);
assert_not_equals(sender, null);
pc.removeTrack(sender);
pc.removeTrack(sender);
t.done();
}))
.catch(t.step_func(reason => {
assert_unreached(reason);
}));
}, 'removeTrack() twice is safe.');
</script>