RTCPeerConnection-setRemoteDescription.html (6077B)
1 <!doctype html> 2 <meta charset=utf-8> 3 <title>RTCPeerConnection.prototype.setRemoteDescription</title> 4 <script src="/resources/testharness.js"></script> 5 <script src="/resources/testharnessreport.js"></script> 6 <script src="RTCPeerConnection-helper.js"></script> 7 <script> 8 'use strict'; 9 10 // Test is based on the following editor draft: 11 // https://w3c.github.io/webrtc-pc/archives/20170605/webrtc.html 12 13 // The following helper functions are called from RTCPeerConnection-helper.js: 14 // assert_session_desc_not_similar() 15 // assert_session_desc_similar() 16 17 /* 18 4.3.2. Interface Definition 19 [Constructor(optional RTCConfiguration configuration)] 20 interface RTCPeerConnection : EventTarget { 21 Promise<void> setRemoteDescription( 22 RTCSessionDescriptionInit description); 23 24 readonly attribute RTCSessionDescription? remoteDescription; 25 readonly attribute RTCSessionDescription? currentRemoteDescription; 26 readonly attribute RTCSessionDescription? pendingRemoteDescription; 27 ... 28 }; 29 30 4.6.2. RTCSessionDescription Class 31 dictionary RTCSessionDescriptionInit { 32 required RTCSdpType type; 33 DOMString sdp = ""; 34 }; 35 36 4.6.1. RTCSdpType 37 enum RTCSdpType { 38 "offer", 39 "pranswer", 40 "answer", 41 "rollback" 42 }; 43 */ 44 45 /* 46 4.6.1. enum RTCSdpType 47 */ 48 promise_test(async t => { 49 const pc = new RTCPeerConnection(); 50 t.add_cleanup(() => pc.close()); 51 52 // SDP is validated after WebIDL validation 53 try { 54 await pc.setRemoteDescription({ type: 'bogus', sdp: 'bogus' }); 55 t.unreached_func("Should have rejected."); 56 } catch (e) { 57 assert_throws_js(TypeError, () => { throw e }); 58 } 59 }, 'setRemoteDescription with invalid type and invalid SDP should reject with TypeError'); 60 61 promise_test(async t => { 62 const pc = new RTCPeerConnection(); 63 t.add_cleanup(() => pc.close()); 64 65 // SDP is validated after validating type 66 try { 67 await pc.setRemoteDescription({ type: 'answer', sdp: 'invalid' }); 68 t.unreached_func("Should have rejected."); 69 } catch (e) { 70 assert_throws_dom('InvalidStateError', () => { throw e }); 71 } 72 }, 'setRemoteDescription() with invalid SDP and stable state should reject with InvalidStateError'); 73 74 /* Dedicated signalingstate events test. */ 75 76 promise_test(async t => { 77 const pc = new RTCPeerConnection(); 78 const pc2 = new RTCPeerConnection(); 79 t.add_cleanup(() => pc.close()); 80 t.add_cleanup(() => pc2.close()); 81 82 let eventCount = 0; 83 const states = [ 84 'stable', 'have-local-offer', 'stable', 'have-remote-offer', 85 ]; 86 pc.onsignalingstatechange = t.step_func(() => 87 assert_equals(pc.signalingState, states[++eventCount])); 88 89 const assert_state = state => { 90 assert_equals(state, pc.signalingState); 91 assert_equals(state, states[eventCount]); 92 }; 93 94 const offer = await generateAudioReceiveOnlyOffer(pc); 95 assert_state('stable'); 96 await pc.setLocalDescription(offer); 97 assert_state('have-local-offer'); 98 await pc2.setRemoteDescription(offer); 99 await exchangeAnswer(pc, pc2); 100 assert_state('stable'); 101 await exchangeOffer(pc2, pc); 102 assert_state('have-remote-offer'); 103 }, 'Negotiation should fire signalingsstate events'); 104 105 /* Operations after returning to stable state */ 106 107 promise_test(async t => { 108 const pc = new RTCPeerConnection(); 109 const pc2 = new RTCPeerConnection(); 110 t.add_cleanup(() => pc.close()); 111 t.add_cleanup(() => pc2.close()); 112 113 const offer1 = await generateAudioReceiveOnlyOffer(pc2); 114 await pc2.setLocalDescription(offer1); 115 await pc.setRemoteDescription(offer1); 116 await exchangeAnswer(pc2, pc); 117 const offer2 = await generateVideoReceiveOnlyOffer(pc2); 118 await pc2.setLocalDescription(offer2); 119 await pc.setRemoteDescription(offer2); 120 assert_session_desc_not_similar(offer1, offer2); 121 assert_session_desc_similar(pc.remoteDescription, offer2); 122 assert_session_desc_similar(pc.currentRemoteDescription, offer1); 123 assert_session_desc_similar(pc.pendingRemoteDescription, offer2); 124 }, 'Calling setRemoteDescription() again after one round of remote-offer/local-answer should succeed'); 125 126 promise_test(async t => { 127 const pc = new RTCPeerConnection(); 128 const pc2 = new RTCPeerConnection(); 129 t.add_cleanup(() => pc.close()); 130 t.add_cleanup(() => pc2.close()); 131 132 const offer = await generateAudioReceiveOnlyOffer(pc); 133 await pc.setLocalDescription(offer); 134 await pc2.setRemoteDescription(offer); 135 const answer = await pc2.createAnswer(); 136 await pc2.setLocalDescription(answer); 137 await pc.setRemoteDescription(answer); 138 await exchangeOffer(pc2, pc); 139 assert_equals(pc.remoteDescription, pc.pendingRemoteDescription); 140 assert_session_desc_similar(pc.remoteDescription, offer); 141 assert_session_desc_similar(pc.currentRemoteDescription, answer); 142 }, 'Switching role from offerer to answerer after going back to stable state should succeed'); 143 144 promise_test(async t => { 145 const pc = new RTCPeerConnection(); 146 t.add_cleanup(() => pc.close()); 147 const offer = await pc.createOffer(); 148 const p = Promise.race([ 149 pc.setRemoteDescription(offer), 150 new Promise(r => t.step_timeout(() => r("timeout"), 200)) 151 ]); 152 pc.close(); 153 assert_equals(await p, "timeout"); 154 assert_equals(pc.signalingState, "closed", "In closed state"); 155 }, 'Closing on setRemoteDescription() neither resolves nor rejects'); 156 157 promise_test(async t => { 158 const pc = new RTCPeerConnection(); 159 t.add_cleanup(() => pc.close()); 160 const offer = await pc.createOffer(); 161 await pc.setLocalDescription(offer); 162 const p = Promise.race([ 163 pc.setRemoteDescription(offer), 164 new Promise(r => t.step_timeout(() => r("timeout"), 200)) 165 ]); 166 pc.close(); 167 assert_equals(await p, "timeout"); 168 assert_equals(pc.signalingState, "closed", "In closed state"); 169 }, 'Closing on rollback neither resolves nor rejects'); 170 171 </script>