<!doctype html>
<meta name="timeout" content="long">
<title>RTCConfiguration iceTransportPolicy</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="RTCConfiguration-helper.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/20170605/webrtc.html
// The following helper function is called from RTCConfiguration-helper.js:
// config_test
/*
[Constructor(optional RTCConfiguration configuration)]
interface RTCPeerConnection : EventTarget {
RTCConfiguration getConfiguration();
void setConfiguration(RTCConfiguration configuration);
...
};
dictionary RTCConfiguration {
sequence<RTCIceServer> iceServers;
RTCIceTransportPolicy iceTransportPolicy = "all";
};
enum RTCIceTransportPolicy {
"relay",
"all"
};
*/
test(() => {
const pc = new RTCPeerConnection();
assert_equals(pc.getConfiguration().iceTransportPolicy, 'all');
}, `new RTCPeerConnection() should have default iceTransportPolicy all`);
test(() => {
const pc = new RTCPeerConnection({ iceTransportPolicy: undefined });
assert_equals(pc.getConfiguration().iceTransportPolicy, 'all');
}, `new RTCPeerConnection({ iceTransportPolicy: undefined }) should have default iceTransportPolicy all`);
test(() => {
const pc = new RTCPeerConnection({ iceTransportPolicy: 'all' });
assert_equals(pc.getConfiguration().iceTransportPolicy, 'all');
}, `new RTCPeerConnection({ iceTransportPolicy: 'all' }) should succeed`);
test(() => {
const pc = new RTCPeerConnection({ iceTransportPolicy: 'relay' });
assert_equals(pc.getConfiguration().iceTransportPolicy, 'relay');
}, `new RTCPeerConnection({ iceTransportPolicy: 'relay' }) should succeed`);
/*
4.3.2. Set a configuration
8. Set the ICE Agent's ICE transports setting to the value of
configuration.iceTransportPolicy. As defined in [JSEP] (section 4.1.16.),
if the new ICE transports setting changes the existing setting, no action
will be taken until the next gathering phase. If a script wants this to
happen immediately, it should do an ICE restart.
*/
test(() => {
const pc = new RTCPeerConnection({ iceTransportPolicy: 'all' });
assert_equals(pc.getConfiguration().iceTransportPolicy, 'all');
pc.setConfiguration({ iceTransportPolicy: 'relay' });
assert_equals(pc.getConfiguration().iceTransportPolicy, 'relay');
}, `setConfiguration({ iceTransportPolicy: 'relay' }) with initial iceTransportPolicy all should succeed`);
test(() => {
const pc = new RTCPeerConnection({ iceTransportPolicy: 'relay' });
assert_equals(pc.getConfiguration().iceTransportPolicy, 'relay');
pc.setConfiguration({ iceTransportPolicy: 'all' });
assert_equals(pc.getConfiguration().iceTransportPolicy, 'all');
}, `setConfiguration({ iceTransportPolicy: 'all' }) with initial iceTransportPolicy relay should succeed`);
test(() => {
const pc = new RTCPeerConnection({ iceTransportPolicy: 'relay' });
assert_equals(pc.getConfiguration().iceTransportPolicy, 'relay');
// default value for iceTransportPolicy is all
pc.setConfiguration({});
assert_equals(pc.getConfiguration().iceTransportPolicy, 'all');
}, `setConfiguration({}) with initial iceTransportPolicy relay should set new value to all`);
config_test(makePc => {
assert_throws_js(TypeError, () =>
makePc({ iceTransportPolicy: 'invalid' }));
}, `with invalid iceTransportPolicy should throw TypeError`);
// "none" is in Blink and Gecko's IDL, but not in the spec.
config_test(makePc => {
assert_throws_js(TypeError, () =>
makePc({ iceTransportPolicy: 'none' }));
}, `with none iceTransportPolicy should throw TypeError`);
config_test(makePc => {
assert_throws_js(TypeError, () =>
makePc({ iceTransportPolicy: null }));
}, `with null iceTransportPolicy should throw TypeError`);
// iceTransportPolicy is called iceTransports in Blink.
test(() => {
const pc = new RTCPeerConnection({ iceTransports: 'relay' });
assert_equals(pc.getConfiguration().iceTransportPolicy, 'all');
}, `new RTCPeerConnection({ iceTransports: 'relay' }) should have no effect`);
test(() => {
const pc = new RTCPeerConnection({ iceTransports: 'invalid' });
assert_equals(pc.getConfiguration().iceTransportPolicy, 'all');
}, `new RTCPeerConnection({ iceTransports: 'invalid' }) should have no effect`);
test(() => {
const pc = new RTCPeerConnection({ iceTransports: null });
assert_equals(pc.getConfiguration().iceTransportPolicy, 'all');
}, `new RTCPeerConnection({ iceTransports: null }) should have no effect`);
const getLines = (sdp, startsWith) =>
sdp.split('\r\n').filter(l => l.startsWith(startsWith));
const getUfrags = ({sdp}) => getLines(sdp, 'a=ice-ufrag:');
promise_test(async t => {
const offerer = new RTCPeerConnection({iceTransportPolicy: 'relay'});
t.add_cleanup(() => offerer.close());
offerer.addEventListener('icecandidate',
e => {
if (e.candidate) {
assert_equals(e.candidate.candidate, '', 'Should get no ICE candidates')
}
}
);
offerer.addTransceiver('audio');
await offerer.setLocalDescription();
await waitForIceGatheringState(offerer, ['complete']);
}, `iceTransportPolicy "relay" on offerer should prevent candidate gathering`);
promise_test(async t => {
const offerer = new RTCPeerConnection();
const answerer = new RTCPeerConnection({iceTransportPolicy: 'relay'});
t.add_cleanup(() => offerer.close());
t.add_cleanup(() => answerer.close());
offerer.addEventListener('icecandidate',
e => {
if (e.candidate) {
assert_equals(e.candidate.candidate, '', 'Should get no ICE candidates')
}
}
);
offerer.addTransceiver('audio');
const offer = await offerer.createOffer();
await answerer.setRemoteDescription(offer);
await answerer.setLocalDescription(await answerer.createAnswer());
await waitForIceGatheringState(answerer, ['complete']);
}, `iceTransportPolicy "relay" on answerer should prevent candidate gathering`);
promise_test(async t => {
const offerer = new RTCPeerConnection();
const answerer = new RTCPeerConnection();
t.add_cleanup(() => offerer.close());
t.add_cleanup(() => answerer.close());
offerer.addTransceiver('audio');
exchangeIceCandidates(offerer, answerer);
await Promise.all([
exchangeOfferAnswer(offerer, answerer),
listenToIceConnected(offerer),
listenToIceConnected(answerer),
waitForIceGatheringState(offerer, ['complete']),
waitForIceGatheringState(answerer, ['complete'])
]);
const [oldUfrag] = getUfrags(offerer.localDescription);
offerer.setConfiguration({iceTransportPolicy: 'relay'});
offerer.addEventListener('icecandidate',
e => {
if (e.candidate) {
assert_equals(e.candidate.candidate, '', 'Should get no ICE candidates')
}
}
);
await Promise.all([
exchangeOfferAnswer(offerer, answerer),
waitForIceStateChange(offerer, ['failed']),
waitForIceStateChange(answerer, ['failed']),
waitForIceGatheringState(offerer, ['complete']),
waitForIceGatheringState(answerer, ['complete'])
]);
const [newUfrag] = getUfrags(offerer.localDescription);
assert_not_equals(oldUfrag, newUfrag,
'Changing iceTransportPolicy should prompt an ICE restart');
}, `Changing iceTransportPolicy from "all" to "relay" causes an ICE restart which should fail, with no new candidates`);
promise_test(async t => {
const offerer = new RTCPeerConnection({iceTransportPolicy: 'relay'});
const answerer = new RTCPeerConnection();
t.add_cleanup(() => offerer.close());
t.add_cleanup(() => answerer.close());
offerer.addTransceiver('audio');
exchangeIceCandidates(offerer, answerer);
const checkNoCandidate =
e => {
if (e.candidate) {
assert_equals(e.candidate.candidate, '', 'Should get no ICE candidates')
}
};
offerer.addEventListener('icecandidate', checkNoCandidate);
await Promise.all([
exchangeOfferAnswer(offerer, answerer),
waitForIceStateChange(offerer, ['failed']),
waitForIceStateChange(answerer, ['failed']),
waitForIceGatheringState(offerer, ['complete']),
waitForIceGatheringState(answerer, ['complete'])
]);
const [oldUfrag] = getUfrags(offerer.localDescription);
offerer.setConfiguration({iceTransportPolicy: 'all'});
offerer.removeEventListener('icecandidate', checkNoCandidate);
await Promise.all([
exchangeOfferAnswer(offerer, answerer),
listenToIceConnected(offerer),
listenToIceConnected(answerer),
waitForIceGatheringState(offerer, ['complete']),
waitForIceGatheringState(answerer, ['complete'])
]);
const [newUfrag] = getUfrags(offerer.localDescription);
assert_not_equals(oldUfrag, newUfrag,
'Changing iceTransportPolicy should prompt an ICE restart');
}, `Changing iceTransportPolicy from "relay" to "all" causes an ICE restart which should succeed`);
promise_test(async t => {
const offerer = new RTCPeerConnection();
const answerer = new RTCPeerConnection();
t.add_cleanup(() => offerer.close());
t.add_cleanup(() => answerer.close());
offerer.addTransceiver('audio');
exchangeIceCandidates(offerer, answerer);
await Promise.all([
exchangeOfferAnswer(offerer, answerer),
listenToIceConnected(offerer),
listenToIceConnected(answerer),
waitForIceGatheringState(offerer, ['complete']),
waitForIceGatheringState(answerer, ['complete'])
]);
const [oldUfrag] = getUfrags(offerer.localDescription);
offerer.setConfiguration({iceTransportPolicy: 'relay'});
offerer.setConfiguration({iceTransportPolicy: 'all'});
await Promise.all([
exchangeOfferAnswer(offerer, answerer),
listenToIceConnected(offerer),
listenToIceConnected(answerer),
waitForIceGatheringState(offerer, ['complete']),
waitForIceGatheringState(answerer, ['complete'])
]);
const [newUfrag] = getUfrags(offerer.localDescription);
assert_not_equals(oldUfrag, newUfrag,
'Changing iceTransportPolicy should prompt an ICE restart');
}, `Changing iceTransportPolicy from "all" to "relay", and back to "all" prompts an ICE restart`);
promise_test(async t => {
const offerer = new RTCPeerConnection();
const answerer = new RTCPeerConnection();
t.add_cleanup(() => offerer.close());
t.add_cleanup(() => answerer.close());
offerer.addTransceiver('audio');
exchangeIceCandidates(offerer, answerer);
await Promise.all([
exchangeOfferAnswer(offerer, answerer),
listenToIceConnected(offerer),
listenToIceConnected(answerer),
waitForIceGatheringState(offerer, ['complete']),
waitForIceGatheringState(answerer, ['complete'])
]);
const [oldUfrag] = getUfrags(answerer.localDescription);
answerer.setConfiguration({iceTransportPolicy: 'relay'});
await Promise.all([
exchangeOfferAnswer(offerer, answerer),
listenToIceConnected(offerer),
listenToIceConnected(answerer),
waitForIceGatheringState(offerer, ['complete']),
waitForIceGatheringState(answerer, ['complete'])
]);
const [newUfrag] = getUfrags(answerer.localDescription);
assert_equals(oldUfrag, newUfrag,
'Changing iceTransportPolicy on answerer should not effect ufrag');
}, `Changing iceTransportPolicy from "all" to "relay" on the answerer has no effect on a subsequent offer/answer`);
</script>