blob: 93f14c87698c890e4762941979cc21deb1ea5585 [file] [log] [blame]
Zhi Huange818b6e2018-02-22 15:26:27 -08001/*
2 * Copyright 2018 The WebRTC Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include <map>
12#include <memory>
13
14#include "p2p/base/fakedtlstransport.h"
15#include "p2p/base/fakeicetransport.h"
16#include "p2p/base/transportfactoryinterface.h"
17#include "p2p/base/transportinfo.h"
18#include "pc/jseptransportcontroller.h"
19#include "rtc_base/gunit.h"
20#include "rtc_base/ptr_util.h"
21#include "rtc_base/thread.h"
22#include "test/gtest.h"
23
24using cricket::FakeDtlsTransport;
25using cricket::Candidate;
26using cricket::Candidates;
27using webrtc::SdpType;
28
29static const int kTimeout = 100;
30static const char kIceUfrag1[] = "u0001";
31static const char kIcePwd1[] = "TESTICEPWD00000000000001";
32static const char kIceUfrag2[] = "u0002";
33static const char kIcePwd2[] = "TESTICEPWD00000000000002";
34static const char kIceUfrag3[] = "u0003";
35static const char kIcePwd3[] = "TESTICEPWD00000000000003";
36static const char kAudioMid1[] = "audio1";
37static const char kAudioMid2[] = "audio2";
38static const char kVideoMid1[] = "video1";
39static const char kVideoMid2[] = "video2";
40static const char kDataMid1[] = "data1";
41
42namespace webrtc {
43
44class FakeTransportFactory : public cricket::TransportFactoryInterface {
45 public:
46 std::unique_ptr<cricket::IceTransportInternal> CreateIceTransport(
47 const std::string& transport_name,
48 int component) override {
49 return rtc::MakeUnique<cricket::FakeIceTransport>(transport_name,
50 component);
51 }
52
53 std::unique_ptr<cricket::DtlsTransportInternal> CreateDtlsTransport(
54 std::unique_ptr<cricket::IceTransportInternal> ice,
55 const rtc::CryptoOptions& crypto_options) override {
56 std::unique_ptr<cricket::FakeIceTransport> fake_ice(
57 static_cast<cricket::FakeIceTransport*>(ice.release()));
58 return rtc::MakeUnique<FakeDtlsTransport>(std::move(fake_ice));
59 }
60};
61
Zhi Huang365381f2018-04-13 16:44:34 -070062class JsepTransportControllerTest : public JsepTransportController::Observer,
63 public testing::Test,
Zhi Huange818b6e2018-02-22 15:26:27 -080064 public sigslot::has_slots<> {
65 public:
66 JsepTransportControllerTest() : signaling_thread_(rtc::Thread::Current()) {
67 fake_transport_factory_ = rtc::MakeUnique<FakeTransportFactory>();
68 }
69
70 void CreateJsepTransportController(
71 JsepTransportController::Config config,
72 rtc::Thread* signaling_thread = rtc::Thread::Current(),
73 rtc::Thread* network_thread = rtc::Thread::Current(),
74 cricket::PortAllocator* port_allocator = nullptr) {
Zhi Huang365381f2018-04-13 16:44:34 -070075 config.transport_observer = this;
Zhi Huange818b6e2018-02-22 15:26:27 -080076 // The tests only works with |fake_transport_factory|;
77 config.external_transport_factory = fake_transport_factory_.get();
78 transport_controller_ = rtc::MakeUnique<JsepTransportController>(
79 signaling_thread, network_thread, port_allocator, config);
80 ConnectTransportControllerSignals();
81 }
82
83 void ConnectTransportControllerSignals() {
84 transport_controller_->SignalIceConnectionState.connect(
85 this, &JsepTransportControllerTest::OnConnectionState);
86 transport_controller_->SignalIceGatheringState.connect(
87 this, &JsepTransportControllerTest::OnGatheringState);
88 transport_controller_->SignalIceCandidatesGathered.connect(
89 this, &JsepTransportControllerTest::OnCandidatesGathered);
Zhi Huange818b6e2018-02-22 15:26:27 -080090 }
91
92 std::unique_ptr<cricket::SessionDescription>
93 CreateSessionDescriptionWithoutBundle() {
94 auto description = rtc::MakeUnique<cricket::SessionDescription>();
95 AddAudioSection(description.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
96 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
97 nullptr);
98 AddVideoSection(description.get(), kVideoMid1, kIceUfrag1, kIcePwd1,
99 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
100 nullptr);
101 return description;
102 }
103
104 std::unique_ptr<cricket::SessionDescription>
105 CreateSessionDescriptionWithBundleGroup() {
106 auto description = CreateSessionDescriptionWithoutBundle();
107 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
108 bundle_group.AddContentName(kAudioMid1);
109 bundle_group.AddContentName(kVideoMid1);
110 description->AddGroup(bundle_group);
111
112 return description;
113 }
114
115 void AddAudioSection(cricket::SessionDescription* description,
116 const std::string& mid,
117 const std::string& ufrag,
118 const std::string& pwd,
119 cricket::IceMode ice_mode,
120 cricket::ConnectionRole conn_role,
121 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
122 std::unique_ptr<cricket::AudioContentDescription> audio(
123 new cricket::AudioContentDescription());
Zhi Huange830e682018-03-30 10:48:35 -0700124 // Set RTCP-mux to be true because the default policy is "mux required".
125 audio->set_rtcp_mux(true);
Zhi Huange818b6e2018-02-22 15:26:27 -0800126 description->AddContent(mid, cricket::MediaProtocolType::kRtp,
127 /*rejected=*/false, audio.release());
128 AddTransportInfo(description, mid, ufrag, pwd, ice_mode, conn_role, cert);
129 }
130
131 void AddVideoSection(cricket::SessionDescription* description,
132 const std::string& mid,
133 const std::string& ufrag,
134 const std::string& pwd,
135 cricket::IceMode ice_mode,
136 cricket::ConnectionRole conn_role,
137 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
138 std::unique_ptr<cricket::VideoContentDescription> video(
139 new cricket::VideoContentDescription());
Zhi Huange830e682018-03-30 10:48:35 -0700140 // Set RTCP-mux to be true because the default policy is "mux required".
141 video->set_rtcp_mux(true);
Zhi Huange818b6e2018-02-22 15:26:27 -0800142 description->AddContent(mid, cricket::MediaProtocolType::kRtp,
143 /*rejected=*/false, video.release());
144 AddTransportInfo(description, mid, ufrag, pwd, ice_mode, conn_role, cert);
145 }
146
147 void AddDataSection(cricket::SessionDescription* description,
148 const std::string& mid,
149 cricket::MediaProtocolType protocol_type,
150 const std::string& ufrag,
151 const std::string& pwd,
152 cricket::IceMode ice_mode,
153 cricket::ConnectionRole conn_role,
154 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
155 std::unique_ptr<cricket::DataContentDescription> data(
156 new cricket::DataContentDescription());
Zhi Huange830e682018-03-30 10:48:35 -0700157 data->set_rtcp_mux(true);
Zhi Huange818b6e2018-02-22 15:26:27 -0800158 description->AddContent(mid, protocol_type,
159 /*rejected=*/false, data.release());
160 AddTransportInfo(description, mid, ufrag, pwd, ice_mode, conn_role, cert);
161 }
162
163 void AddTransportInfo(cricket::SessionDescription* description,
164 const std::string& mid,
165 const std::string& ufrag,
166 const std::string& pwd,
167 cricket::IceMode ice_mode,
168 cricket::ConnectionRole conn_role,
169 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
170 std::unique_ptr<rtc::SSLFingerprint> fingerprint;
171 if (cert) {
172 fingerprint.reset(rtc::SSLFingerprint::CreateFromCertificate(cert));
173 }
174
175 cricket::TransportDescription transport_desc(std::vector<std::string>(),
176 ufrag, pwd, ice_mode,
177 conn_role, fingerprint.get());
178 description->AddTransportInfo(cricket::TransportInfo(mid, transport_desc));
179 }
180
181 cricket::IceConfig CreateIceConfig(
182 int receiving_timeout,
183 cricket::ContinualGatheringPolicy continual_gathering_policy) {
184 cricket::IceConfig config;
185 config.receiving_timeout = receiving_timeout;
186 config.continual_gathering_policy = continual_gathering_policy;
187 return config;
188 }
189
190 Candidate CreateCandidate(const std::string& transport_name, int component) {
191 Candidate c;
192 c.set_transport_name(transport_name);
193 c.set_address(rtc::SocketAddress("192.168.1.1", 8000));
194 c.set_component(component);
195 c.set_protocol(cricket::UDP_PROTOCOL_NAME);
196 c.set_priority(1);
197 return c;
198 }
199
200 void CreateLocalDescriptionAndCompleteConnectionOnNetworkThread() {
201 if (!network_thread_->IsCurrent()) {
202 network_thread_->Invoke<void>(RTC_FROM_HERE, [&] {
203 CreateLocalDescriptionAndCompleteConnectionOnNetworkThread();
204 });
205 return;
206 }
207
208 auto description = CreateSessionDescriptionWithBundleGroup();
209 EXPECT_TRUE(transport_controller_
210 ->SetLocalDescription(SdpType::kOffer, description.get())
211 .ok());
212
213 transport_controller_->MaybeStartGathering();
214 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
215 transport_controller_->GetDtlsTransport(kAudioMid1));
216 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
217 transport_controller_->GetDtlsTransport(kVideoMid1));
218 fake_audio_dtls->fake_ice_transport()->SignalCandidateGathered(
219 fake_audio_dtls->fake_ice_transport(),
220 CreateCandidate(kAudioMid1, /*component=*/1));
221 fake_video_dtls->fake_ice_transport()->SignalCandidateGathered(
222 fake_video_dtls->fake_ice_transport(),
223 CreateCandidate(kVideoMid1, /*component=*/1));
224 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
225 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
226 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(2);
227 fake_video_dtls->fake_ice_transport()->SetConnectionCount(2);
228 fake_audio_dtls->SetReceiving(true);
229 fake_video_dtls->SetReceiving(true);
230 fake_audio_dtls->SetWritable(true);
231 fake_video_dtls->SetWritable(true);
232 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
233 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
234 }
235
236 protected:
237 void OnConnectionState(cricket::IceConnectionState state) {
238 if (!signaling_thread_->IsCurrent()) {
239 signaled_on_non_signaling_thread_ = true;
240 }
241 connection_state_ = state;
242 ++connection_state_signal_count_;
243 }
244
245 void OnGatheringState(cricket::IceGatheringState state) {
246 if (!signaling_thread_->IsCurrent()) {
247 signaled_on_non_signaling_thread_ = true;
248 }
249 gathering_state_ = state;
250 ++gathering_state_signal_count_;
251 }
252
253 void OnCandidatesGathered(const std::string& transport_name,
254 const Candidates& candidates) {
255 if (!signaling_thread_->IsCurrent()) {
256 signaled_on_non_signaling_thread_ = true;
257 }
258 candidates_[transport_name].insert(candidates_[transport_name].end(),
259 candidates.begin(), candidates.end());
260 ++candidates_signal_count_;
261 }
262
Zhi Huang365381f2018-04-13 16:44:34 -0700263 // JsepTransportController::Observer overrides.
Taylor Brandstettercbaa2542018-04-16 16:42:14 -0700264 bool OnTransportChanged(
Zhi Huang365381f2018-04-13 16:44:34 -0700265 const std::string& mid,
Taylor Brandstettercbaa2542018-04-16 16:42:14 -0700266 RtpTransportInternal* rtp_transport,
Zhi Huang365381f2018-04-13 16:44:34 -0700267 cricket::DtlsTransportInternal* dtls_transport) override {
Taylor Brandstettercbaa2542018-04-16 16:42:14 -0700268 changed_rtp_transport_by_mid_[mid] = rtp_transport;
Zhi Huange818b6e2018-02-22 15:26:27 -0800269 changed_dtls_transport_by_mid_[mid] = dtls_transport;
Taylor Brandstettercbaa2542018-04-16 16:42:14 -0700270 return true;
Zhi Huange818b6e2018-02-22 15:26:27 -0800271 }
272
273 // Information received from signals from transport controller.
274 cricket::IceConnectionState connection_state_ =
275 cricket::kIceConnectionConnecting;
276 bool receiving_ = false;
277 cricket::IceGatheringState gathering_state_ = cricket::kIceGatheringNew;
278 // transport_name => candidates
279 std::map<std::string, Candidates> candidates_;
280 // Counts of each signal emitted.
281 int connection_state_signal_count_ = 0;
282 int receiving_signal_count_ = 0;
283 int gathering_state_signal_count_ = 0;
284 int candidates_signal_count_ = 0;
285
286 // |network_thread_| should be destroyed after |transport_controller_|
287 std::unique_ptr<rtc::Thread> network_thread_;
288 std::unique_ptr<JsepTransportController> transport_controller_;
289 std::unique_ptr<FakeTransportFactory> fake_transport_factory_;
290 rtc::Thread* const signaling_thread_ = nullptr;
291 bool signaled_on_non_signaling_thread_ = false;
292 // Used to verify the SignalRtpTransportChanged/SignalDtlsTransportChanged are
293 // signaled correctly.
294 std::map<std::string, RtpTransportInternal*> changed_rtp_transport_by_mid_;
295 std::map<std::string, cricket::DtlsTransportInternal*>
296 changed_dtls_transport_by_mid_;
297};
298
299TEST_F(JsepTransportControllerTest, GetRtpTransport) {
300 CreateJsepTransportController(JsepTransportController::Config());
301 auto description = CreateSessionDescriptionWithoutBundle();
302 EXPECT_TRUE(transport_controller_
303 ->SetLocalDescription(SdpType::kOffer, description.get())
304 .ok());
305 auto audio_rtp_transport = transport_controller_->GetRtpTransport(kAudioMid1);
306 auto video_rtp_transport = transport_controller_->GetRtpTransport(kVideoMid1);
307 EXPECT_NE(nullptr, audio_rtp_transport);
308 EXPECT_NE(nullptr, video_rtp_transport);
309 EXPECT_NE(audio_rtp_transport, video_rtp_transport);
310 // Return nullptr for non-existing ones.
311 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kAudioMid2));
312}
313
314TEST_F(JsepTransportControllerTest, GetDtlsTransport) {
315 JsepTransportController::Config config;
316 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
317 CreateJsepTransportController(config);
318 auto description = CreateSessionDescriptionWithoutBundle();
319 EXPECT_TRUE(transport_controller_
320 ->SetLocalDescription(SdpType::kOffer, description.get())
321 .ok());
322 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kAudioMid1));
323 EXPECT_NE(nullptr, transport_controller_->GetRtcpDtlsTransport(kAudioMid1));
324 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kVideoMid1));
325 EXPECT_NE(nullptr, transport_controller_->GetRtcpDtlsTransport(kVideoMid1));
326 // Return nullptr for non-existing ones.
327 EXPECT_EQ(nullptr, transport_controller_->GetDtlsTransport(kVideoMid2));
328 EXPECT_EQ(nullptr, transport_controller_->GetRtcpDtlsTransport(kVideoMid2));
329}
330
Zhi Huange830e682018-03-30 10:48:35 -0700331TEST_F(JsepTransportControllerTest, GetDtlsTransportWithRtcpMux) {
332 JsepTransportController::Config config;
333 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
334 CreateJsepTransportController(config);
335 auto description = CreateSessionDescriptionWithoutBundle();
336 EXPECT_TRUE(transport_controller_
337 ->SetLocalDescription(SdpType::kOffer, description.get())
338 .ok());
339 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kAudioMid1));
340 EXPECT_EQ(nullptr, transport_controller_->GetRtcpDtlsTransport(kAudioMid1));
341 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kVideoMid1));
342 EXPECT_EQ(nullptr, transport_controller_->GetRtcpDtlsTransport(kVideoMid1));
343}
344
Zhi Huange818b6e2018-02-22 15:26:27 -0800345TEST_F(JsepTransportControllerTest, SetIceConfig) {
346 CreateJsepTransportController(JsepTransportController::Config());
347 auto description = CreateSessionDescriptionWithoutBundle();
348 EXPECT_TRUE(transport_controller_
349 ->SetLocalDescription(SdpType::kOffer, description.get())
350 .ok());
351
352 transport_controller_->SetIceConfig(
353 CreateIceConfig(kTimeout, cricket::GATHER_CONTINUALLY));
354 FakeDtlsTransport* fake_audio_dtls = static_cast<FakeDtlsTransport*>(
355 transport_controller_->GetDtlsTransport(kAudioMid1));
356 ASSERT_NE(nullptr, fake_audio_dtls);
357 EXPECT_EQ(kTimeout,
358 fake_audio_dtls->fake_ice_transport()->receiving_timeout());
359 EXPECT_TRUE(fake_audio_dtls->fake_ice_transport()->gather_continually());
360
361 // Test that value stored in controller is applied to new transports.
362 AddAudioSection(description.get(), kAudioMid2, kIceUfrag1, kIcePwd1,
363 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
364 nullptr);
365
366 EXPECT_TRUE(transport_controller_
367 ->SetLocalDescription(SdpType::kOffer, description.get())
368 .ok());
369 fake_audio_dtls = static_cast<FakeDtlsTransport*>(
370 transport_controller_->GetDtlsTransport(kAudioMid2));
371 ASSERT_NE(nullptr, fake_audio_dtls);
372 EXPECT_EQ(kTimeout,
373 fake_audio_dtls->fake_ice_transport()->receiving_timeout());
374 EXPECT_TRUE(fake_audio_dtls->fake_ice_transport()->gather_continually());
375}
376
377// Tests the getter and setter of the ICE restart flag.
378TEST_F(JsepTransportControllerTest, NeedIceRestart) {
379 CreateJsepTransportController(JsepTransportController::Config());
380 auto description = CreateSessionDescriptionWithoutBundle();
381 EXPECT_TRUE(transport_controller_
382 ->SetLocalDescription(SdpType::kOffer, description.get())
383 .ok());
384 EXPECT_TRUE(transport_controller_
385 ->SetRemoteDescription(SdpType::kAnswer, description.get())
386 .ok());
387
388 // Initially NeedsIceRestart should return false.
389 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kAudioMid1));
390 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kVideoMid1));
391 // Set the needs-ice-restart flag and verify NeedsIceRestart starts returning
392 // true.
393 transport_controller_->SetNeedsIceRestartFlag();
394 EXPECT_TRUE(transport_controller_->NeedsIceRestart(kAudioMid1));
395 EXPECT_TRUE(transport_controller_->NeedsIceRestart(kVideoMid1));
396 // For a nonexistent transport, false should be returned.
397 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kVideoMid2));
398
399 // Reset the ice_ufrag/ice_pwd for audio.
400 auto audio_transport_info = description->GetTransportInfoByName(kAudioMid1);
401 audio_transport_info->description.ice_ufrag = kIceUfrag2;
402 audio_transport_info->description.ice_pwd = kIcePwd2;
403 EXPECT_TRUE(transport_controller_
404 ->SetLocalDescription(SdpType::kOffer, description.get())
405 .ok());
406 // Because the ICE is only restarted for audio, NeedsIceRestart is expected to
407 // return false for audio and true for video.
408 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kAudioMid1));
409 EXPECT_TRUE(transport_controller_->NeedsIceRestart(kVideoMid1));
410}
411
412TEST_F(JsepTransportControllerTest, MaybeStartGathering) {
413 CreateJsepTransportController(JsepTransportController::Config());
414 auto description = CreateSessionDescriptionWithBundleGroup();
415 EXPECT_TRUE(transport_controller_
416 ->SetLocalDescription(SdpType::kOffer, description.get())
417 .ok());
418 // After setting the local description, we should be able to start gathering
419 // candidates.
420 transport_controller_->MaybeStartGathering();
421 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
422 EXPECT_EQ(1, gathering_state_signal_count_);
423}
424
425TEST_F(JsepTransportControllerTest, AddRemoveRemoteCandidates) {
426 CreateJsepTransportController(JsepTransportController::Config());
427 auto description = CreateSessionDescriptionWithoutBundle();
428 transport_controller_->SetLocalDescription(SdpType::kOffer,
429 description.get());
430 transport_controller_->SetRemoteDescription(SdpType::kAnswer,
431 description.get());
432 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
433 transport_controller_->GetDtlsTransport(kAudioMid1));
434 ASSERT_NE(nullptr, fake_audio_dtls);
435 Candidates candidates;
436 candidates.push_back(
437 CreateCandidate(kAudioMid1, cricket::ICE_CANDIDATE_COMPONENT_RTP));
438 EXPECT_TRUE(
439 transport_controller_->AddRemoteCandidates(kAudioMid1, candidates).ok());
440 EXPECT_EQ(1U,
441 fake_audio_dtls->fake_ice_transport()->remote_candidates().size());
442
443 EXPECT_TRUE(transport_controller_->RemoveRemoteCandidates(candidates).ok());
444 EXPECT_EQ(0U,
445 fake_audio_dtls->fake_ice_transport()->remote_candidates().size());
446}
447
448TEST_F(JsepTransportControllerTest, SetAndGetLocalCertificate) {
449 CreateJsepTransportController(JsepTransportController::Config());
450
451 rtc::scoped_refptr<rtc::RTCCertificate> certificate1 =
452 rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
453 rtc::SSLIdentity::Generate("session1", rtc::KT_DEFAULT)));
454 rtc::scoped_refptr<rtc::RTCCertificate> returned_certificate;
455
456 auto description = rtc::MakeUnique<cricket::SessionDescription>();
457 AddAudioSection(description.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
458 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
459 certificate1);
460
461 // Apply the local certificate.
462 EXPECT_TRUE(transport_controller_->SetLocalCertificate(certificate1));
463 // Apply the local description.
464 EXPECT_TRUE(transport_controller_
465 ->SetLocalDescription(SdpType::kOffer, description.get())
466 .ok());
467 returned_certificate = transport_controller_->GetLocalCertificate(kAudioMid1);
468 EXPECT_TRUE(returned_certificate);
469 EXPECT_EQ(certificate1->identity()->certificate().ToPEMString(),
470 returned_certificate->identity()->certificate().ToPEMString());
471
472 // Should fail if called for a nonexistant transport.
473 EXPECT_EQ(nullptr, transport_controller_->GetLocalCertificate(kVideoMid1));
474
475 // Shouldn't be able to change the identity once set.
476 rtc::scoped_refptr<rtc::RTCCertificate> certificate2 =
477 rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
478 rtc::SSLIdentity::Generate("session2", rtc::KT_DEFAULT)));
479 EXPECT_FALSE(transport_controller_->SetLocalCertificate(certificate2));
480}
481
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800482TEST_F(JsepTransportControllerTest, GetRemoteSSLCertChain) {
Zhi Huange818b6e2018-02-22 15:26:27 -0800483 CreateJsepTransportController(JsepTransportController::Config());
484 auto description = CreateSessionDescriptionWithBundleGroup();
485 EXPECT_TRUE(transport_controller_
486 ->SetLocalDescription(SdpType::kOffer, description.get())
487 .ok());
488 rtc::FakeSSLCertificate fake_certificate("fake_data");
489
490 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
491 transport_controller_->GetDtlsTransport(kAudioMid1));
492 fake_audio_dtls->SetRemoteSSLCertificate(&fake_certificate);
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800493 std::unique_ptr<rtc::SSLCertChain> returned_cert_chain =
494 transport_controller_->GetRemoteSSLCertChain(kAudioMid1);
495 ASSERT_TRUE(returned_cert_chain);
496 ASSERT_EQ(1u, returned_cert_chain->GetSize());
Zhi Huange818b6e2018-02-22 15:26:27 -0800497 EXPECT_EQ(fake_certificate.ToPEMString(),
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800498 returned_cert_chain->Get(0).ToPEMString());
Zhi Huange818b6e2018-02-22 15:26:27 -0800499
500 // Should fail if called for a nonexistant transport.
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800501 EXPECT_FALSE(transport_controller_->GetRemoteSSLCertChain(kAudioMid2));
Zhi Huange818b6e2018-02-22 15:26:27 -0800502}
503
504TEST_F(JsepTransportControllerTest, GetDtlsRole) {
505 CreateJsepTransportController(JsepTransportController::Config());
506 auto offer_certificate =
507 rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
508 rtc::SSLIdentity::Generate("offer", rtc::KT_DEFAULT)));
509 auto answer_certificate =
510 rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
511 rtc::SSLIdentity::Generate("answer", rtc::KT_DEFAULT)));
512 transport_controller_->SetLocalCertificate(offer_certificate);
513
514 auto offer_desc = rtc::MakeUnique<cricket::SessionDescription>();
515 AddAudioSection(offer_desc.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
516 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
517 offer_certificate);
518 auto answer_desc = rtc::MakeUnique<cricket::SessionDescription>();
519 AddAudioSection(answer_desc.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
520 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
521 answer_certificate);
522
523 EXPECT_TRUE(transport_controller_
524 ->SetLocalDescription(SdpType::kOffer, offer_desc.get())
525 .ok());
526
Danil Chapovalov66cadcc2018-06-19 16:47:43 +0200527 absl::optional<rtc::SSLRole> role =
Zhi Huange818b6e2018-02-22 15:26:27 -0800528 transport_controller_->GetDtlsRole(kAudioMid1);
529 // The DTLS role is not decided yet.
530 EXPECT_FALSE(role);
531 EXPECT_TRUE(transport_controller_
532 ->SetRemoteDescription(SdpType::kAnswer, answer_desc.get())
533 .ok());
534 role = transport_controller_->GetDtlsRole(kAudioMid1);
535
536 ASSERT_TRUE(role);
537 EXPECT_EQ(rtc::SSL_CLIENT, *role);
538}
539
540TEST_F(JsepTransportControllerTest, GetStats) {
541 CreateJsepTransportController(JsepTransportController::Config());
542 auto description = CreateSessionDescriptionWithBundleGroup();
543 EXPECT_TRUE(transport_controller_
544 ->SetLocalDescription(SdpType::kOffer, description.get())
545 .ok());
546
547 cricket::TransportStats stats;
548 EXPECT_TRUE(transport_controller_->GetStats(kAudioMid1, &stats));
549 EXPECT_EQ(kAudioMid1, stats.transport_name);
550 EXPECT_EQ(1u, stats.channel_stats.size());
551 // Return false for non-existing transport.
552 EXPECT_FALSE(transport_controller_->GetStats(kAudioMid2, &stats));
553}
554
555TEST_F(JsepTransportControllerTest, SignalConnectionStateFailed) {
556 CreateJsepTransportController(JsepTransportController::Config());
557 auto description = CreateSessionDescriptionWithoutBundle();
558 EXPECT_TRUE(transport_controller_
559 ->SetLocalDescription(SdpType::kOffer, description.get())
560 .ok());
561
562 auto fake_ice = static_cast<cricket::FakeIceTransport*>(
563 transport_controller_->GetDtlsTransport(kAudioMid1)->ice_transport());
564 fake_ice->SetCandidatesGatheringComplete();
565 fake_ice->SetConnectionCount(1);
566 // The connection stats will be failed if there is no active connection.
567 fake_ice->SetConnectionCount(0);
568 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
569 EXPECT_EQ(1, connection_state_signal_count_);
570}
571
572TEST_F(JsepTransportControllerTest, SignalConnectionStateConnected) {
573 CreateJsepTransportController(JsepTransportController::Config());
574 auto description = CreateSessionDescriptionWithoutBundle();
575 EXPECT_TRUE(transport_controller_
576 ->SetLocalDescription(SdpType::kOffer, description.get())
577 .ok());
578
579 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
580 transport_controller_->GetDtlsTransport(kAudioMid1));
581 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
582 transport_controller_->GetDtlsTransport(kVideoMid1));
583
584 // First, have one transport connect, and another fail, to ensure that
585 // the first transport connecting didn't trigger a "connected" state signal.
586 // We should only get a signal when all are connected.
587 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
588 fake_audio_dtls->SetWritable(true);
589 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
590 // Decrease the number of the connection to trigger the signal.
591 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
592 fake_video_dtls->fake_ice_transport()->SetConnectionCount(0);
593 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
594
595 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
596 EXPECT_EQ(1, connection_state_signal_count_);
597
598 // Set the connection count to be 2 and the cricket::FakeIceTransport will set
599 // the transport state to be STATE_CONNECTING.
600 fake_video_dtls->fake_ice_transport()->SetConnectionCount(2);
601 fake_video_dtls->SetWritable(true);
602 EXPECT_EQ_WAIT(cricket::kIceConnectionConnected, connection_state_, kTimeout);
603 EXPECT_EQ(2, connection_state_signal_count_);
604}
605
606TEST_F(JsepTransportControllerTest, SignalConnectionStateComplete) {
607 CreateJsepTransportController(JsepTransportController::Config());
608 auto description = CreateSessionDescriptionWithoutBundle();
609 EXPECT_TRUE(transport_controller_
610 ->SetLocalDescription(SdpType::kOffer, description.get())
611 .ok());
612
613 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
614 transport_controller_->GetDtlsTransport(kAudioMid1));
615 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
616 transport_controller_->GetDtlsTransport(kVideoMid1));
617
618 // First, have one transport connect, and another fail, to ensure that
619 // the first transport connecting didn't trigger a "connected" state signal.
620 // We should only get a signal when all are connected.
621 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
622 fake_audio_dtls->SetWritable(true);
623 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
624 // Decrease the number of the connection to trigger the signal.
625 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
626 fake_video_dtls->fake_ice_transport()->SetConnectionCount(0);
627 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
628
629 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
630 EXPECT_EQ(1, connection_state_signal_count_);
631
632 // Set the connection count to be 1 and the cricket::FakeIceTransport will set
633 // the transport state to be STATE_COMPLETED.
634 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
635 fake_video_dtls->SetWritable(true);
636 EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
637 EXPECT_EQ(2, connection_state_signal_count_);
638}
639
640TEST_F(JsepTransportControllerTest, SignalIceGatheringStateGathering) {
641 CreateJsepTransportController(JsepTransportController::Config());
642 auto description = CreateSessionDescriptionWithoutBundle();
643 EXPECT_TRUE(transport_controller_
644 ->SetLocalDescription(SdpType::kOffer, description.get())
645 .ok());
646
647 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
648 transport_controller_->GetDtlsTransport(kAudioMid1));
649 fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
650 // Should be in the gathering state as soon as any transport starts gathering.
651 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
652 EXPECT_EQ(1, gathering_state_signal_count_);
653}
654
655TEST_F(JsepTransportControllerTest, SignalIceGatheringStateComplete) {
656 CreateJsepTransportController(JsepTransportController::Config());
657 auto description = CreateSessionDescriptionWithoutBundle();
658 EXPECT_TRUE(transport_controller_
659 ->SetLocalDescription(SdpType::kOffer, description.get())
660 .ok());
661
662 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
663 transport_controller_->GetDtlsTransport(kAudioMid1));
664 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
665 transport_controller_->GetDtlsTransport(kVideoMid1));
666
667 fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
668 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
669 EXPECT_EQ(1, gathering_state_signal_count_);
670
671 // Have one transport finish gathering, to make sure gathering
672 // completion wasn't signalled if only one transport finished gathering.
673 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
674 EXPECT_EQ(1, gathering_state_signal_count_);
675
676 fake_video_dtls->fake_ice_transport()->MaybeStartGathering();
677 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
678 EXPECT_EQ(1, gathering_state_signal_count_);
679
680 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
681 EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
682 EXPECT_EQ(2, gathering_state_signal_count_);
683}
684
685// Test that when the last transport that hasn't finished connecting and/or
686// gathering is destroyed, the aggregate state jumps to "completed". This can
687// happen if, for example, we have an audio and video transport, the audio
688// transport completes, then we start bundling video on the audio transport.
689TEST_F(JsepTransportControllerTest,
690 SignalingWhenLastIncompleteTransportDestroyed) {
691 CreateJsepTransportController(JsepTransportController::Config());
692 auto description = CreateSessionDescriptionWithBundleGroup();
693 EXPECT_TRUE(transport_controller_
694 ->SetLocalDescription(SdpType::kOffer, description.get())
695 .ok());
696
697 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
698 transport_controller_->GetDtlsTransport(kAudioMid1));
699 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
700 transport_controller_->GetDtlsTransport(kVideoMid1));
701 EXPECT_NE(fake_audio_dtls, fake_video_dtls);
702
703 fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
704 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
705 EXPECT_EQ(1, gathering_state_signal_count_);
706
707 // Let the audio transport complete.
708 fake_audio_dtls->SetWritable(true);
709 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
710 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
711 EXPECT_EQ(1, gathering_state_signal_count_);
712
713 // Set the remote description and enable the bundle.
714 EXPECT_TRUE(transport_controller_
715 ->SetRemoteDescription(SdpType::kAnswer, description.get())
716 .ok());
717 // The BUNDLE should be enabled, the incomplete video transport should be
718 // deleted and the states shoud be updated.
719 fake_video_dtls = static_cast<FakeDtlsTransport*>(
720 transport_controller_->GetDtlsTransport(kVideoMid1));
721 EXPECT_EQ(fake_audio_dtls, fake_video_dtls);
722 EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
723 EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
724 EXPECT_EQ(2, gathering_state_signal_count_);
725}
726
727TEST_F(JsepTransportControllerTest, SignalCandidatesGathered) {
728 CreateJsepTransportController(JsepTransportController::Config());
729 auto description = CreateSessionDescriptionWithBundleGroup();
730 EXPECT_TRUE(transport_controller_
731 ->SetLocalDescription(SdpType::kOffer, description.get())
732 .ok());
733 transport_controller_->MaybeStartGathering();
734
735 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
736 transport_controller_->GetDtlsTransport(kAudioMid1));
737 fake_audio_dtls->fake_ice_transport()->SignalCandidateGathered(
738 fake_audio_dtls->fake_ice_transport(), CreateCandidate(kAudioMid1, 1));
739 EXPECT_EQ_WAIT(1, candidates_signal_count_, kTimeout);
740 EXPECT_EQ(1u, candidates_[kAudioMid1].size());
741}
742
743TEST_F(JsepTransportControllerTest, IceSignalingOccursOnSignalingThread) {
744 network_thread_ = rtc::Thread::CreateWithSocketServer();
745 network_thread_->Start();
746 CreateJsepTransportController(JsepTransportController::Config(),
747 signaling_thread_, network_thread_.get(),
748 /*PortAllocator=*/nullptr);
749 CreateLocalDescriptionAndCompleteConnectionOnNetworkThread();
750
751 // connecting --> connected --> completed
752 EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
753 EXPECT_EQ(2, connection_state_signal_count_);
754
755 // new --> gathering --> complete
756 EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
757 EXPECT_EQ(2, gathering_state_signal_count_);
758
759 EXPECT_EQ_WAIT(1u, candidates_[kAudioMid1].size(), kTimeout);
760 EXPECT_EQ_WAIT(1u, candidates_[kVideoMid1].size(), kTimeout);
761 EXPECT_EQ(2, candidates_signal_count_);
762
763 EXPECT_TRUE(!signaled_on_non_signaling_thread_);
764}
765
766// Older versions of Chrome expect the ICE role to be re-determined when an
767// ICE restart occurs, and also don't perform conflict resolution correctly,
768// so for now we can't safely stop doing this.
769// See: https://bugs.chromium.org/p/chromium/issues/detail?id=628676
770// TODO(deadbeef): Remove this when these old versions of Chrome reach a low
771// enough population.
772TEST_F(JsepTransportControllerTest, IceRoleRedeterminedOnIceRestartByDefault) {
773 CreateJsepTransportController(JsepTransportController::Config());
774 // Let the |transport_controller_| be the controlled side initially.
775 auto remote_offer = rtc::MakeUnique<cricket::SessionDescription>();
776 AddAudioSection(remote_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
777 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
778 nullptr);
779 auto local_answer = rtc::MakeUnique<cricket::SessionDescription>();
780 AddAudioSection(local_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
781 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
782 nullptr);
783
784 EXPECT_TRUE(transport_controller_
785 ->SetRemoteDescription(SdpType::kOffer, remote_offer.get())
786 .ok());
787 EXPECT_TRUE(transport_controller_
788 ->SetLocalDescription(SdpType::kAnswer, local_answer.get())
789 .ok());
790
791 auto fake_dtls = static_cast<FakeDtlsTransport*>(
792 transport_controller_->GetDtlsTransport(kAudioMid1));
793 EXPECT_EQ(cricket::ICEROLE_CONTROLLED,
794 fake_dtls->fake_ice_transport()->GetIceRole());
795
796 // New offer will trigger the ICE restart.
797 auto restart_local_offer = rtc::MakeUnique<cricket::SessionDescription>();
798 AddAudioSection(restart_local_offer.get(), kAudioMid1, kIceUfrag3, kIcePwd3,
799 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
800 nullptr);
801 EXPECT_TRUE(
802 transport_controller_
803 ->SetLocalDescription(SdpType::kOffer, restart_local_offer.get())
804 .ok());
805 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
806 fake_dtls->fake_ice_transport()->GetIceRole());
807}
808
809// Test that if the TransportController was created with the
810// |redetermine_role_on_ice_restart| parameter set to false, the role is *not*
811// redetermined on an ICE restart.
812TEST_F(JsepTransportControllerTest, IceRoleNotRedetermined) {
813 JsepTransportController::Config config;
814 config.redetermine_role_on_ice_restart = false;
815
816 CreateJsepTransportController(config);
817 // Let the |transport_controller_| be the controlled side initially.
818 auto remote_offer = rtc::MakeUnique<cricket::SessionDescription>();
819 AddAudioSection(remote_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
820 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
821 nullptr);
822 auto local_answer = rtc::MakeUnique<cricket::SessionDescription>();
823 AddAudioSection(local_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
824 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
825 nullptr);
826
827 EXPECT_TRUE(transport_controller_
828 ->SetRemoteDescription(SdpType::kOffer, remote_offer.get())
829 .ok());
830 EXPECT_TRUE(transport_controller_
831 ->SetLocalDescription(SdpType::kAnswer, local_answer.get())
832 .ok());
833
834 auto fake_dtls = static_cast<FakeDtlsTransport*>(
835 transport_controller_->GetDtlsTransport(kAudioMid1));
836 EXPECT_EQ(cricket::ICEROLE_CONTROLLED,
837 fake_dtls->fake_ice_transport()->GetIceRole());
838
839 // New offer will trigger the ICE restart.
840 auto restart_local_offer = rtc::MakeUnique<cricket::SessionDescription>();
841 AddAudioSection(restart_local_offer.get(), kAudioMid1, kIceUfrag3, kIcePwd3,
842 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
843 nullptr);
844 EXPECT_TRUE(
845 transport_controller_
846 ->SetLocalDescription(SdpType::kOffer, restart_local_offer.get())
847 .ok());
848 EXPECT_EQ(cricket::ICEROLE_CONTROLLED,
849 fake_dtls->fake_ice_transport()->GetIceRole());
850}
851
852// Tests ICE-Lite mode in remote answer.
853TEST_F(JsepTransportControllerTest, SetIceRoleWhenIceLiteInRemoteAnswer) {
854 CreateJsepTransportController(JsepTransportController::Config());
855 auto local_offer = rtc::MakeUnique<cricket::SessionDescription>();
856 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
857 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
858 nullptr);
859 EXPECT_TRUE(transport_controller_
860 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
861 .ok());
862 auto fake_dtls = static_cast<FakeDtlsTransport*>(
863 transport_controller_->GetDtlsTransport(kAudioMid1));
864 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
865 fake_dtls->fake_ice_transport()->GetIceRole());
866 EXPECT_EQ(cricket::ICEMODE_FULL,
867 fake_dtls->fake_ice_transport()->remote_ice_mode());
868
869 auto remote_answer = rtc::MakeUnique<cricket::SessionDescription>();
870 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
871 cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_PASSIVE,
872 nullptr);
873 EXPECT_TRUE(transport_controller_
874 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
875 .ok());
876 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
877 fake_dtls->fake_ice_transport()->GetIceRole());
878 EXPECT_EQ(cricket::ICEMODE_LITE,
879 fake_dtls->fake_ice_transport()->remote_ice_mode());
880}
881
882// Tests that the ICE role remains "controlling" if a subsequent offer that
883// does an ICE restart is received from an ICE lite endpoint. Regression test
884// for: https://crbug.com/710760
885TEST_F(JsepTransportControllerTest,
886 IceRoleIsControllingAfterIceRestartFromIceLiteEndpoint) {
887 CreateJsepTransportController(JsepTransportController::Config());
888 auto remote_offer = rtc::MakeUnique<cricket::SessionDescription>();
889 AddAudioSection(remote_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
890 cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_ACTPASS,
891 nullptr);
892 auto local_answer = rtc::MakeUnique<cricket::SessionDescription>();
893 AddAudioSection(local_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
894 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
895 nullptr);
896 // Initial Offer/Answer exchange. If the remote offerer is ICE-Lite, then the
897 // local side is the controlling.
898 EXPECT_TRUE(transport_controller_
899 ->SetRemoteDescription(SdpType::kOffer, remote_offer.get())
900 .ok());
901 EXPECT_TRUE(transport_controller_
902 ->SetLocalDescription(SdpType::kAnswer, local_answer.get())
903 .ok());
904 auto fake_dtls = static_cast<FakeDtlsTransport*>(
905 transport_controller_->GetDtlsTransport(kAudioMid1));
906 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
907 fake_dtls->fake_ice_transport()->GetIceRole());
908
909 // In the subsequence remote offer triggers an ICE restart.
910 auto remote_offer2 = rtc::MakeUnique<cricket::SessionDescription>();
911 AddAudioSection(remote_offer2.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
912 cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_ACTPASS,
913 nullptr);
914 auto local_answer2 = rtc::MakeUnique<cricket::SessionDescription>();
915 AddAudioSection(local_answer2.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
916 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
917 nullptr);
918 EXPECT_TRUE(transport_controller_
919 ->SetRemoteDescription(SdpType::kOffer, remote_offer2.get())
920 .ok());
921 EXPECT_TRUE(transport_controller_
922 ->SetLocalDescription(SdpType::kAnswer, local_answer2.get())
923 .ok());
924 fake_dtls = static_cast<FakeDtlsTransport*>(
925 transport_controller_->GetDtlsTransport(kAudioMid1));
926 // The local side is still the controlling role since the remote side is using
927 // ICE-Lite.
928 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
929 fake_dtls->fake_ice_transport()->GetIceRole());
930}
931
932// Tests that the SDP has more than one audio/video m= sections.
933TEST_F(JsepTransportControllerTest, MultipleMediaSectionsOfSameTypeWithBundle) {
934 CreateJsepTransportController(JsepTransportController::Config());
935 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
936 bundle_group.AddContentName(kAudioMid1);
937 bundle_group.AddContentName(kAudioMid2);
938 bundle_group.AddContentName(kVideoMid1);
939 bundle_group.AddContentName(kDataMid1);
940
941 auto local_offer = rtc::MakeUnique<cricket::SessionDescription>();
942 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
943 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
944 nullptr);
945 AddAudioSection(local_offer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
946 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
947 nullptr);
948 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
949 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
950 nullptr);
951 AddDataSection(local_offer.get(), kDataMid1,
952 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
953 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
954 nullptr);
955
956 auto remote_answer = rtc::MakeUnique<cricket::SessionDescription>();
957 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
958 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
959 nullptr);
960 AddAudioSection(remote_answer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
961 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
962 nullptr);
963 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
964 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
965 nullptr);
966 AddDataSection(remote_answer.get(), kDataMid1,
967 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
968 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
969 nullptr);
970
971 local_offer->AddGroup(bundle_group);
972 remote_answer->AddGroup(bundle_group);
973
974 EXPECT_TRUE(transport_controller_
975 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
976 .ok());
977 EXPECT_TRUE(transport_controller_
978 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
979 .ok());
980 // Verify that all the sections are bundled on kAudio1.
981 auto transport1 = transport_controller_->GetRtpTransport(kAudioMid1);
982 auto transport2 = transport_controller_->GetRtpTransport(kAudioMid2);
983 auto transport3 = transport_controller_->GetRtpTransport(kVideoMid1);
984 auto transport4 = transport_controller_->GetRtpTransport(kDataMid1);
985 EXPECT_EQ(transport1, transport2);
986 EXPECT_EQ(transport1, transport3);
987 EXPECT_EQ(transport1, transport4);
988
989 // Verify the OnRtpTransport/DtlsTransportChanged signals are fired correctly.
990 auto it = changed_rtp_transport_by_mid_.find(kAudioMid2);
991 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
992 EXPECT_EQ(transport1, it->second);
993 it = changed_rtp_transport_by_mid_.find(kAudioMid2);
994 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
995 EXPECT_EQ(transport1, it->second);
996 it = changed_rtp_transport_by_mid_.find(kVideoMid1);
997 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
998 EXPECT_EQ(transport1, it->second);
999 // Verify the DtlsTransport for the SCTP data channel is reset correctly.
1000 auto it2 = changed_dtls_transport_by_mid_.find(kDataMid1);
1001 ASSERT_TRUE(it2 != changed_dtls_transport_by_mid_.end());
1002 EXPECT_EQ(transport1->rtp_packet_transport(), it2->second);
1003}
1004
1005// Tests that only a subset of all the m= sections are bundled.
1006TEST_F(JsepTransportControllerTest, BundleSubsetOfMediaSections) {
1007 CreateJsepTransportController(JsepTransportController::Config());
1008 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1009 bundle_group.AddContentName(kAudioMid1);
1010 bundle_group.AddContentName(kVideoMid1);
1011
1012 auto local_offer = rtc::MakeUnique<cricket::SessionDescription>();
1013 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1014 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1015 nullptr);
1016 AddAudioSection(local_offer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
1017 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1018 nullptr);
1019 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1020 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1021 nullptr);
1022
1023 auto remote_answer = rtc::MakeUnique<cricket::SessionDescription>();
1024 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1025 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1026 nullptr);
1027 AddAudioSection(remote_answer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
1028 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1029 nullptr);
1030 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1031 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1032 nullptr);
1033
1034 local_offer->AddGroup(bundle_group);
1035 remote_answer->AddGroup(bundle_group);
1036 EXPECT_TRUE(transport_controller_
1037 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1038 .ok());
1039 EXPECT_TRUE(transport_controller_
1040 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1041 .ok());
1042
1043 // Verifiy that only |kAudio1| and |kVideo1| are bundled.
1044 auto transport1 = transport_controller_->GetRtpTransport(kAudioMid1);
1045 auto transport2 = transport_controller_->GetRtpTransport(kAudioMid2);
1046 auto transport3 = transport_controller_->GetRtpTransport(kVideoMid1);
1047 EXPECT_NE(transport1, transport2);
1048 EXPECT_EQ(transport1, transport3);
1049
1050 auto it = changed_rtp_transport_by_mid_.find(kVideoMid1);
1051 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1052 EXPECT_EQ(transport1, it->second);
1053 it = changed_rtp_transport_by_mid_.find(kAudioMid2);
Zhi Huangd2248f82018-04-10 14:41:03 -07001054 EXPECT_TRUE(transport2 == it->second);
Zhi Huange818b6e2018-02-22 15:26:27 -08001055}
1056
1057// Tests that the initial offer/answer only have data section and audio/video
1058// sections are added in the subsequent offer.
1059TEST_F(JsepTransportControllerTest, BundleOnDataSectionInSubsequentOffer) {
1060 CreateJsepTransportController(JsepTransportController::Config());
1061 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1062 bundle_group.AddContentName(kDataMid1);
1063
1064 auto local_offer = rtc::MakeUnique<cricket::SessionDescription>();
1065 AddDataSection(local_offer.get(), kDataMid1,
1066 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
1067 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1068 nullptr);
1069 auto remote_answer = rtc::MakeUnique<cricket::SessionDescription>();
1070 AddDataSection(remote_answer.get(), kDataMid1,
1071 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
1072 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1073 nullptr);
1074 local_offer->AddGroup(bundle_group);
1075 remote_answer->AddGroup(bundle_group);
1076
1077 EXPECT_TRUE(transport_controller_
1078 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1079 .ok());
1080 EXPECT_TRUE(transport_controller_
1081 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1082 .ok());
1083 auto data_transport = transport_controller_->GetRtpTransport(kDataMid1);
1084
1085 // Add audio/video sections in subsequent offer.
1086 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1087 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1088 nullptr);
1089 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1090 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1091 nullptr);
1092 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1093 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1094 nullptr);
1095 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1096 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1097 nullptr);
1098
1099 // Reset the bundle group and do another offer/answer exchange.
1100 bundle_group.AddContentName(kAudioMid1);
1101 bundle_group.AddContentName(kVideoMid1);
1102 local_offer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1103 remote_answer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1104 local_offer->AddGroup(bundle_group);
1105 remote_answer->AddGroup(bundle_group);
1106
1107 EXPECT_TRUE(transport_controller_
1108 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1109 .ok());
1110 EXPECT_TRUE(transport_controller_
1111 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1112 .ok());
1113
1114 auto audio_transport = transport_controller_->GetRtpTransport(kAudioMid1);
1115 auto video_transport = transport_controller_->GetRtpTransport(kVideoMid1);
1116 EXPECT_EQ(data_transport, audio_transport);
1117 EXPECT_EQ(data_transport, video_transport);
1118}
1119
1120TEST_F(JsepTransportControllerTest, VideoDataRejectedInAnswer) {
1121 CreateJsepTransportController(JsepTransportController::Config());
1122 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1123 bundle_group.AddContentName(kAudioMid1);
1124 bundle_group.AddContentName(kVideoMid1);
1125 bundle_group.AddContentName(kDataMid1);
1126
1127 auto local_offer = rtc::MakeUnique<cricket::SessionDescription>();
1128 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1129 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1130 nullptr);
1131 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1132 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1133 nullptr);
1134 AddDataSection(local_offer.get(), kDataMid1,
1135 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1136 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1137 nullptr);
1138
1139 auto remote_answer = rtc::MakeUnique<cricket::SessionDescription>();
1140 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1141 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1142 nullptr);
1143 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1144 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1145 nullptr);
1146 AddDataSection(remote_answer.get(), kDataMid1,
1147 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1148 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1149 nullptr);
1150 // Reject video and data section.
1151 remote_answer->contents()[1].rejected = true;
1152 remote_answer->contents()[2].rejected = true;
1153
1154 local_offer->AddGroup(bundle_group);
1155 remote_answer->AddGroup(bundle_group);
1156
1157 EXPECT_TRUE(transport_controller_
1158 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1159 .ok());
1160 EXPECT_TRUE(transport_controller_
1161 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1162 .ok());
1163
1164 // Verify the RtpTransport/DtlsTransport is destroyed correctly.
1165 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kVideoMid1));
1166 EXPECT_EQ(nullptr, transport_controller_->GetDtlsTransport(kDataMid1));
1167 // Verify the signals are fired correctly.
1168 auto it = changed_rtp_transport_by_mid_.find(kVideoMid1);
1169 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1170 EXPECT_EQ(nullptr, it->second);
1171 auto it2 = changed_dtls_transport_by_mid_.find(kDataMid1);
1172 ASSERT_TRUE(it2 != changed_dtls_transport_by_mid_.end());
1173 EXPECT_EQ(nullptr, it2->second);
1174}
1175
1176// Tests that changing the bundled MID in subsequent offer/answer exchange is
1177// not supported.
1178// TODO(bugs.webrtc.org/6704): Change this test to expect success once issue is
1179// fixed
1180TEST_F(JsepTransportControllerTest, ChangeBundledMidNotSupported) {
1181 CreateJsepTransportController(JsepTransportController::Config());
1182 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1183 bundle_group.AddContentName(kAudioMid1);
1184 bundle_group.AddContentName(kVideoMid1);
1185
1186 auto local_offer = rtc::MakeUnique<cricket::SessionDescription>();
1187 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1188 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1189 nullptr);
1190 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1191 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1192 nullptr);
1193
1194 auto remote_answer = rtc::MakeUnique<cricket::SessionDescription>();
1195 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1196 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1197 nullptr);
1198 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1199 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1200 nullptr);
1201
1202 local_offer->AddGroup(bundle_group);
1203 remote_answer->AddGroup(bundle_group);
1204 EXPECT_TRUE(transport_controller_
1205 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1206 .ok());
1207 EXPECT_TRUE(transport_controller_
1208 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1209 .ok());
1210 EXPECT_EQ(transport_controller_->GetRtpTransport(kAudioMid1),
1211 transport_controller_->GetRtpTransport(kVideoMid1));
1212
1213 // Reorder the bundle group.
1214 EXPECT_TRUE(bundle_group.RemoveContentName(kAudioMid1));
1215 bundle_group.AddContentName(kAudioMid1);
1216 // The answerer uses the new bundle group and now the bundle mid is changed to
1217 // |kVideo1|.
1218 remote_answer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1219 remote_answer->AddGroup(bundle_group);
1220 EXPECT_TRUE(transport_controller_
1221 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1222 .ok());
1223 EXPECT_FALSE(transport_controller_
1224 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1225 .ok());
1226}
Zhi Huange830e682018-03-30 10:48:35 -07001227// Test that rejecting only the first m= section of a BUNDLE group is treated as
1228// an error, but rejecting all of them works as expected.
1229TEST_F(JsepTransportControllerTest, RejectFirstContentInBundleGroup) {
1230 CreateJsepTransportController(JsepTransportController::Config());
1231 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1232 bundle_group.AddContentName(kAudioMid1);
1233 bundle_group.AddContentName(kVideoMid1);
1234 bundle_group.AddContentName(kDataMid1);
1235
1236 auto local_offer = rtc::MakeUnique<cricket::SessionDescription>();
1237 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1238 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1239 nullptr);
1240 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1241 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1242 nullptr);
1243 AddDataSection(local_offer.get(), kDataMid1,
1244 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1245 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1246 nullptr);
1247
1248 auto remote_answer = rtc::MakeUnique<cricket::SessionDescription>();
1249 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1250 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1251 nullptr);
1252 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1253 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1254 nullptr);
1255 AddDataSection(remote_answer.get(), kDataMid1,
1256 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1257 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1258 nullptr);
1259 // Reject audio content in answer.
1260 remote_answer->contents()[0].rejected = true;
1261
1262 local_offer->AddGroup(bundle_group);
1263 remote_answer->AddGroup(bundle_group);
1264
1265 EXPECT_TRUE(transport_controller_
1266 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1267 .ok());
1268 EXPECT_FALSE(transport_controller_
1269 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1270 .ok());
1271
1272 // Reject all the contents.
1273 remote_answer->contents()[1].rejected = true;
1274 remote_answer->contents()[2].rejected = true;
1275 EXPECT_TRUE(transport_controller_
1276 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1277 .ok());
1278 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kAudioMid1));
1279 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kVideoMid1));
1280 EXPECT_EQ(nullptr, transport_controller_->GetDtlsTransport(kDataMid1));
1281}
1282
1283// Tests that applying non-RTCP-mux offer would fail when kRtcpMuxPolicyRequire
1284// is used.
1285TEST_F(JsepTransportControllerTest, ApplyNonRtcpMuxOfferWhenMuxingRequired) {
1286 JsepTransportController::Config config;
1287 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
1288 CreateJsepTransportController(config);
1289 auto local_offer = rtc::MakeUnique<cricket::SessionDescription>();
1290 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1291 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1292 nullptr);
1293
1294 local_offer->contents()[0].media_description()->set_rtcp_mux(false);
1295 // Applying a non-RTCP-mux offer is expected to fail.
1296 EXPECT_FALSE(transport_controller_
1297 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1298 .ok());
1299}
1300
1301// Tests that applying non-RTCP-mux answer would fail when kRtcpMuxPolicyRequire
1302// is used.
1303TEST_F(JsepTransportControllerTest, ApplyNonRtcpMuxAnswerWhenMuxingRequired) {
1304 JsepTransportController::Config config;
1305 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
1306 CreateJsepTransportController(config);
1307 auto local_offer = rtc::MakeUnique<cricket::SessionDescription>();
1308 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1309 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1310 nullptr);
1311 EXPECT_TRUE(transport_controller_
1312 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1313 .ok());
1314
1315 auto remote_answer = rtc::MakeUnique<cricket::SessionDescription>();
1316 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1317 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1318 nullptr);
1319 // Applying a non-RTCP-mux answer is expected to fail.
1320 remote_answer->contents()[0].media_description()->set_rtcp_mux(false);
1321 EXPECT_FALSE(transport_controller_
1322 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1323 .ok());
1324}
Zhi Huange818b6e2018-02-22 15:26:27 -08001325
Zhi Huangd2248f82018-04-10 14:41:03 -07001326// This tests that the BUNDLE group in answer should be a subset of the offered
1327// group.
1328TEST_F(JsepTransportControllerTest,
1329 AddContentToBundleGroupInAnswerNotSupported) {
1330 CreateJsepTransportController(JsepTransportController::Config());
1331 auto local_offer = CreateSessionDescriptionWithoutBundle();
1332 auto remote_answer = CreateSessionDescriptionWithoutBundle();
1333
1334 cricket::ContentGroup offer_bundle_group(cricket::GROUP_TYPE_BUNDLE);
1335 offer_bundle_group.AddContentName(kAudioMid1);
1336 local_offer->AddGroup(offer_bundle_group);
1337
1338 cricket::ContentGroup answer_bundle_group(cricket::GROUP_TYPE_BUNDLE);
1339 answer_bundle_group.AddContentName(kAudioMid1);
1340 answer_bundle_group.AddContentName(kVideoMid1);
1341 remote_answer->AddGroup(answer_bundle_group);
1342 EXPECT_TRUE(transport_controller_
1343 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1344 .ok());
1345 EXPECT_FALSE(transport_controller_
1346 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1347 .ok());
1348}
1349
1350// This tests that the BUNDLE group with non-existing MID should be rejectd.
1351TEST_F(JsepTransportControllerTest, RejectBundleGroupWithNonExistingMid) {
1352 CreateJsepTransportController(JsepTransportController::Config());
1353 auto local_offer = CreateSessionDescriptionWithoutBundle();
1354 auto remote_answer = CreateSessionDescriptionWithoutBundle();
1355
1356 cricket::ContentGroup invalid_bundle_group(cricket::GROUP_TYPE_BUNDLE);
1357 // The BUNDLE group is invalid because there is no data section in the
1358 // description.
1359 invalid_bundle_group.AddContentName(kDataMid1);
1360 local_offer->AddGroup(invalid_bundle_group);
1361 remote_answer->AddGroup(invalid_bundle_group);
1362
1363 EXPECT_FALSE(transport_controller_
1364 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1365 .ok());
1366 EXPECT_FALSE(transport_controller_
1367 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1368 .ok());
1369}
1370
1371// This tests that an answer shouldn't be able to remove an m= section from an
1372// established group without rejecting it.
1373TEST_F(JsepTransportControllerTest, RemoveContentFromBundleGroup) {
1374 CreateJsepTransportController(JsepTransportController::Config());
1375
1376 auto local_offer = CreateSessionDescriptionWithBundleGroup();
1377 auto remote_answer = CreateSessionDescriptionWithBundleGroup();
1378 EXPECT_TRUE(transport_controller_
1379 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1380 .ok());
1381 EXPECT_TRUE(transport_controller_
1382 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1383 .ok());
1384
1385 // Do an re-offer/answer.
1386 EXPECT_TRUE(transport_controller_
1387 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1388 .ok());
1389 auto new_answer = CreateSessionDescriptionWithoutBundle();
1390 cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
1391 // The answer removes video from the BUNDLE group without rejecting it is
1392 // invalid.
1393 new_bundle_group.AddContentName(kAudioMid1);
1394 new_answer->AddGroup(new_bundle_group);
1395
1396 // Applying invalid answer is expected to fail.
1397 EXPECT_FALSE(transport_controller_
1398 ->SetRemoteDescription(SdpType::kAnswer, new_answer.get())
1399 .ok());
1400
1401 // Rejected the video content.
1402 auto video_content = new_answer->GetContentByName(kVideoMid1);
1403 ASSERT_TRUE(video_content);
1404 video_content->rejected = true;
1405 EXPECT_TRUE(transport_controller_
1406 ->SetRemoteDescription(SdpType::kAnswer, new_answer.get())
1407 .ok());
1408}
1409
Zhi Huange818b6e2018-02-22 15:26:27 -08001410} // namespace webrtc