171 lines
5.9 KiB
HTML
171 lines
5.9 KiB
HTML
<!doctype html>
|
|
<meta charset=utf-8>
|
|
<title>RTCPeerConnection.prototype.setRemoteDescription</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/20170605/webrtc.html
|
|
|
|
// The following helper functions are called from RTCPeerConnection-helper.js:
|
|
// assert_session_desc_not_similar()
|
|
// assert_session_desc_similar()
|
|
|
|
/*
|
|
4.3.2. Interface Definition
|
|
[Constructor(optional RTCConfiguration configuration)]
|
|
interface RTCPeerConnection : EventTarget {
|
|
Promise<void> setRemoteDescription(
|
|
RTCSessionDescriptionInit description);
|
|
|
|
readonly attribute RTCSessionDescription? remoteDescription;
|
|
readonly attribute RTCSessionDescription? currentRemoteDescription;
|
|
readonly attribute RTCSessionDescription? pendingRemoteDescription;
|
|
...
|
|
};
|
|
|
|
4.6.2. RTCSessionDescription Class
|
|
dictionary RTCSessionDescriptionInit {
|
|
required RTCSdpType type;
|
|
DOMString sdp = "";
|
|
};
|
|
|
|
4.6.1. RTCSdpType
|
|
enum RTCSdpType {
|
|
"offer",
|
|
"pranswer",
|
|
"answer",
|
|
"rollback"
|
|
};
|
|
*/
|
|
|
|
/*
|
|
4.6.1. enum RTCSdpType
|
|
*/
|
|
promise_test(async t => {
|
|
const pc = new RTCPeerConnection();
|
|
t.add_cleanup(() => pc.close());
|
|
|
|
// SDP is validated after WebIDL validation
|
|
try {
|
|
await pc.setRemoteDescription({ type: 'bogus', sdp: 'bogus' });
|
|
t.unreached_func("Should have rejected.");
|
|
} catch (e) {
|
|
assert_throws_js(TypeError, () => { throw e });
|
|
}
|
|
}, 'setRemoteDescription with invalid type and invalid SDP should reject with TypeError');
|
|
|
|
promise_test(async t => {
|
|
const pc = new RTCPeerConnection();
|
|
t.add_cleanup(() => pc.close());
|
|
|
|
// SDP is validated after validating type
|
|
try {
|
|
await pc.setRemoteDescription({ type: 'answer', sdp: 'invalid' });
|
|
t.unreached_func("Should have rejected.");
|
|
} catch (e) {
|
|
assert_throws_dom('InvalidStateError', () => { throw e });
|
|
}
|
|
}, 'setRemoteDescription() with invalid SDP and stable state should reject with InvalidStateError');
|
|
|
|
/* Dedicated signalingstate events test. */
|
|
|
|
promise_test(async t => {
|
|
const pc = new RTCPeerConnection();
|
|
const pc2 = new RTCPeerConnection();
|
|
t.add_cleanup(() => pc.close());
|
|
t.add_cleanup(() => pc2.close());
|
|
|
|
let eventCount = 0;
|
|
const states = [
|
|
'stable', 'have-local-offer', 'stable', 'have-remote-offer',
|
|
];
|
|
pc.onsignalingstatechange = t.step_func(() =>
|
|
assert_equals(pc.signalingState, states[++eventCount]));
|
|
|
|
const assert_state = state => {
|
|
assert_equals(state, pc.signalingState);
|
|
assert_equals(state, states[eventCount]);
|
|
};
|
|
|
|
const offer = await generateAudioReceiveOnlyOffer(pc);
|
|
assert_state('stable');
|
|
await pc.setLocalDescription(offer);
|
|
assert_state('have-local-offer');
|
|
await pc2.setRemoteDescription(offer);
|
|
await exchangeAnswer(pc, pc2);
|
|
assert_state('stable');
|
|
await exchangeOffer(pc2, pc);
|
|
assert_state('have-remote-offer');
|
|
}, 'Negotiation should fire signalingsstate events');
|
|
|
|
/* Operations after returning to stable state */
|
|
|
|
promise_test(async t => {
|
|
const pc = new RTCPeerConnection();
|
|
const pc2 = new RTCPeerConnection();
|
|
t.add_cleanup(() => pc.close());
|
|
t.add_cleanup(() => pc2.close());
|
|
|
|
const offer1 = await generateAudioReceiveOnlyOffer(pc2);
|
|
await pc2.setLocalDescription(offer1);
|
|
await pc.setRemoteDescription(offer1);
|
|
await exchangeAnswer(pc2, pc);
|
|
const offer2 = await generateVideoReceiveOnlyOffer(pc2);
|
|
await pc2.setLocalDescription(offer2);
|
|
await pc.setRemoteDescription(offer2);
|
|
assert_session_desc_not_similar(offer1, offer2);
|
|
assert_session_desc_similar(pc.remoteDescription, offer2);
|
|
assert_session_desc_similar(pc.currentRemoteDescription, offer1);
|
|
assert_session_desc_similar(pc.pendingRemoteDescription, offer2);
|
|
}, 'Calling setRemoteDescription() again after one round of remote-offer/local-answer should succeed');
|
|
|
|
promise_test(async t => {
|
|
const pc = new RTCPeerConnection();
|
|
const pc2 = new RTCPeerConnection();
|
|
t.add_cleanup(() => pc.close());
|
|
t.add_cleanup(() => pc2.close());
|
|
|
|
const offer = await generateAudioReceiveOnlyOffer(pc);
|
|
await pc.setLocalDescription(offer);
|
|
await pc2.setRemoteDescription(offer);
|
|
const answer = await pc2.createAnswer();
|
|
await pc2.setLocalDescription(answer);
|
|
await pc.setRemoteDescription(answer);
|
|
await exchangeOffer(pc2, pc);
|
|
assert_equals(pc.remoteDescription, pc.pendingRemoteDescription);
|
|
assert_session_desc_similar(pc.remoteDescription, offer);
|
|
assert_session_desc_similar(pc.currentRemoteDescription, answer);
|
|
}, 'Switching role from offerer to answerer after going back to stable state should succeed');
|
|
|
|
promise_test(async t => {
|
|
const pc = new RTCPeerConnection();
|
|
t.add_cleanup(() => pc.close());
|
|
const offer = await pc.createOffer();
|
|
const p = Promise.race([
|
|
pc.setRemoteDescription(offer),
|
|
new Promise(r => t.step_timeout(() => r("timeout"), 200))
|
|
]);
|
|
pc.close();
|
|
assert_equals(await p, "timeout");
|
|
assert_equals(pc.signalingState, "closed", "In closed state");
|
|
}, 'Closing on setRemoteDescription() neither resolves nor rejects');
|
|
|
|
promise_test(async t => {
|
|
const pc = new RTCPeerConnection();
|
|
t.add_cleanup(() => pc.close());
|
|
const offer = await pc.createOffer();
|
|
await pc.setLocalDescription(offer);
|
|
const p = Promise.race([
|
|
pc.setRemoteDescription(offer),
|
|
new Promise(r => t.step_timeout(() => r("timeout"), 200))
|
|
]);
|
|
pc.close();
|
|
assert_equals(await p, "timeout");
|
|
assert_equals(pc.signalingState, "closed", "In closed state");
|
|
}, 'Closing on rollback neither resolves nor rejects');
|
|
|
|
</script>
|