blob: 78f4986914299da307721a32d2dc0c49313388b9 [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
62class JsepTransportControllerTest : public testing::Test,
63 public sigslot::has_slots<> {
64 public:
65 JsepTransportControllerTest() : signaling_thread_(rtc::Thread::Current()) {
66 fake_transport_factory_ = rtc::MakeUnique<FakeTransportFactory>();
67 }
68
69 void CreateJsepTransportController(
70 JsepTransportController::Config config,
71 rtc::Thread* signaling_thread = rtc::Thread::Current(),
72 rtc::Thread* network_thread = rtc::Thread::Current(),
73 cricket::PortAllocator* port_allocator = nullptr) {
74 // The tests only works with |fake_transport_factory|;
75 config.external_transport_factory = fake_transport_factory_.get();
76 transport_controller_ = rtc::MakeUnique<JsepTransportController>(
77 signaling_thread, network_thread, port_allocator, config);
78 ConnectTransportControllerSignals();
79 }
80
81 void ConnectTransportControllerSignals() {
82 transport_controller_->SignalIceConnectionState.connect(
83 this, &JsepTransportControllerTest::OnConnectionState);
84 transport_controller_->SignalIceGatheringState.connect(
85 this, &JsepTransportControllerTest::OnGatheringState);
86 transport_controller_->SignalIceCandidatesGathered.connect(
87 this, &JsepTransportControllerTest::OnCandidatesGathered);
88 transport_controller_->SignalRtpTransportChanged.connect(
89 this, &JsepTransportControllerTest::OnRtpTransportChanged);
90 transport_controller_->SignalDtlsTransportChanged.connect(
91 this, &JsepTransportControllerTest::OnDtlsTransportChanged);
92 }
93
94 std::unique_ptr<cricket::SessionDescription>
95 CreateSessionDescriptionWithoutBundle() {
96 auto description = rtc::MakeUnique<cricket::SessionDescription>();
97 AddAudioSection(description.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
98 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
99 nullptr);
100 AddVideoSection(description.get(), kVideoMid1, kIceUfrag1, kIcePwd1,
101 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
102 nullptr);
103 return description;
104 }
105
106 std::unique_ptr<cricket::SessionDescription>
107 CreateSessionDescriptionWithBundleGroup() {
108 auto description = CreateSessionDescriptionWithoutBundle();
109 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
110 bundle_group.AddContentName(kAudioMid1);
111 bundle_group.AddContentName(kVideoMid1);
112 description->AddGroup(bundle_group);
113
114 return description;
115 }
116
117 void AddAudioSection(cricket::SessionDescription* description,
118 const std::string& mid,
119 const std::string& ufrag,
120 const std::string& pwd,
121 cricket::IceMode ice_mode,
122 cricket::ConnectionRole conn_role,
123 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
124 std::unique_ptr<cricket::AudioContentDescription> audio(
125 new cricket::AudioContentDescription());
126 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());
140 description->AddContent(mid, cricket::MediaProtocolType::kRtp,
141 /*rejected=*/false, video.release());
142 AddTransportInfo(description, mid, ufrag, pwd, ice_mode, conn_role, cert);
143 }
144
145 void AddDataSection(cricket::SessionDescription* description,
146 const std::string& mid,
147 cricket::MediaProtocolType protocol_type,
148 const std::string& ufrag,
149 const std::string& pwd,
150 cricket::IceMode ice_mode,
151 cricket::ConnectionRole conn_role,
152 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
153 std::unique_ptr<cricket::DataContentDescription> data(
154 new cricket::DataContentDescription());
155 description->AddContent(mid, protocol_type,
156 /*rejected=*/false, data.release());
157 AddTransportInfo(description, mid, ufrag, pwd, ice_mode, conn_role, cert);
158 }
159
160 void AddTransportInfo(cricket::SessionDescription* description,
161 const std::string& mid,
162 const std::string& ufrag,
163 const std::string& pwd,
164 cricket::IceMode ice_mode,
165 cricket::ConnectionRole conn_role,
166 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
167 std::unique_ptr<rtc::SSLFingerprint> fingerprint;
168 if (cert) {
169 fingerprint.reset(rtc::SSLFingerprint::CreateFromCertificate(cert));
170 }
171
172 cricket::TransportDescription transport_desc(std::vector<std::string>(),
173 ufrag, pwd, ice_mode,
174 conn_role, fingerprint.get());
175 description->AddTransportInfo(cricket::TransportInfo(mid, transport_desc));
176 }
177
178 cricket::IceConfig CreateIceConfig(
179 int receiving_timeout,
180 cricket::ContinualGatheringPolicy continual_gathering_policy) {
181 cricket::IceConfig config;
182 config.receiving_timeout = receiving_timeout;
183 config.continual_gathering_policy = continual_gathering_policy;
184 return config;
185 }
186
187 Candidate CreateCandidate(const std::string& transport_name, int component) {
188 Candidate c;
189 c.set_transport_name(transport_name);
190 c.set_address(rtc::SocketAddress("192.168.1.1", 8000));
191 c.set_component(component);
192 c.set_protocol(cricket::UDP_PROTOCOL_NAME);
193 c.set_priority(1);
194 return c;
195 }
196
197 void CreateLocalDescriptionAndCompleteConnectionOnNetworkThread() {
198 if (!network_thread_->IsCurrent()) {
199 network_thread_->Invoke<void>(RTC_FROM_HERE, [&] {
200 CreateLocalDescriptionAndCompleteConnectionOnNetworkThread();
201 });
202 return;
203 }
204
205 auto description = CreateSessionDescriptionWithBundleGroup();
206 EXPECT_TRUE(transport_controller_
207 ->SetLocalDescription(SdpType::kOffer, description.get())
208 .ok());
209
210 transport_controller_->MaybeStartGathering();
211 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
212 transport_controller_->GetDtlsTransport(kAudioMid1));
213 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
214 transport_controller_->GetDtlsTransport(kVideoMid1));
215 fake_audio_dtls->fake_ice_transport()->SignalCandidateGathered(
216 fake_audio_dtls->fake_ice_transport(),
217 CreateCandidate(kAudioMid1, /*component=*/1));
218 fake_video_dtls->fake_ice_transport()->SignalCandidateGathered(
219 fake_video_dtls->fake_ice_transport(),
220 CreateCandidate(kVideoMid1, /*component=*/1));
221 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
222 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
223 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(2);
224 fake_video_dtls->fake_ice_transport()->SetConnectionCount(2);
225 fake_audio_dtls->SetReceiving(true);
226 fake_video_dtls->SetReceiving(true);
227 fake_audio_dtls->SetWritable(true);
228 fake_video_dtls->SetWritable(true);
229 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
230 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
231 }
232
233 protected:
234 void OnConnectionState(cricket::IceConnectionState state) {
235 if (!signaling_thread_->IsCurrent()) {
236 signaled_on_non_signaling_thread_ = true;
237 }
238 connection_state_ = state;
239 ++connection_state_signal_count_;
240 }
241
242 void OnGatheringState(cricket::IceGatheringState state) {
243 if (!signaling_thread_->IsCurrent()) {
244 signaled_on_non_signaling_thread_ = true;
245 }
246 gathering_state_ = state;
247 ++gathering_state_signal_count_;
248 }
249
250 void OnCandidatesGathered(const std::string& transport_name,
251 const Candidates& candidates) {
252 if (!signaling_thread_->IsCurrent()) {
253 signaled_on_non_signaling_thread_ = true;
254 }
255 candidates_[transport_name].insert(candidates_[transport_name].end(),
256 candidates.begin(), candidates.end());
257 ++candidates_signal_count_;
258 }
259
260 void OnRtpTransportChanged(const std::string& mid,
261 RtpTransportInternal* rtp_transport) {
262 changed_rtp_transport_by_mid_[mid] = rtp_transport;
263 }
264
265 void OnDtlsTransportChanged(const std::string& mid,
266 cricket::DtlsTransportInternal* dtls_transport) {
267 changed_dtls_transport_by_mid_[mid] = dtls_transport;
268 }
269
270 // Information received from signals from transport controller.
271 cricket::IceConnectionState connection_state_ =
272 cricket::kIceConnectionConnecting;
273 bool receiving_ = false;
274 cricket::IceGatheringState gathering_state_ = cricket::kIceGatheringNew;
275 // transport_name => candidates
276 std::map<std::string, Candidates> candidates_;
277 // Counts of each signal emitted.
278 int connection_state_signal_count_ = 0;
279 int receiving_signal_count_ = 0;
280 int gathering_state_signal_count_ = 0;
281 int candidates_signal_count_ = 0;
282
283 // |network_thread_| should be destroyed after |transport_controller_|
284 std::unique_ptr<rtc::Thread> network_thread_;
285 std::unique_ptr<JsepTransportController> transport_controller_;
286 std::unique_ptr<FakeTransportFactory> fake_transport_factory_;
287 rtc::Thread* const signaling_thread_ = nullptr;
288 bool signaled_on_non_signaling_thread_ = false;
289 // Used to verify the SignalRtpTransportChanged/SignalDtlsTransportChanged are
290 // signaled correctly.
291 std::map<std::string, RtpTransportInternal*> changed_rtp_transport_by_mid_;
292 std::map<std::string, cricket::DtlsTransportInternal*>
293 changed_dtls_transport_by_mid_;
294};
295
296TEST_F(JsepTransportControllerTest, GetRtpTransport) {
297 CreateJsepTransportController(JsepTransportController::Config());
298 auto description = CreateSessionDescriptionWithoutBundle();
299 EXPECT_TRUE(transport_controller_
300 ->SetLocalDescription(SdpType::kOffer, description.get())
301 .ok());
302 auto audio_rtp_transport = transport_controller_->GetRtpTransport(kAudioMid1);
303 auto video_rtp_transport = transport_controller_->GetRtpTransport(kVideoMid1);
304 EXPECT_NE(nullptr, audio_rtp_transport);
305 EXPECT_NE(nullptr, video_rtp_transport);
306 EXPECT_NE(audio_rtp_transport, video_rtp_transport);
307 // Return nullptr for non-existing ones.
308 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kAudioMid2));
309}
310
311TEST_F(JsepTransportControllerTest, GetDtlsTransport) {
312 JsepTransportController::Config config;
313 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
314 CreateJsepTransportController(config);
315 auto description = CreateSessionDescriptionWithoutBundle();
316 EXPECT_TRUE(transport_controller_
317 ->SetLocalDescription(SdpType::kOffer, description.get())
318 .ok());
319 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kAudioMid1));
320 EXPECT_NE(nullptr, transport_controller_->GetRtcpDtlsTransport(kAudioMid1));
321 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kVideoMid1));
322 EXPECT_NE(nullptr, transport_controller_->GetRtcpDtlsTransport(kVideoMid1));
323 // Return nullptr for non-existing ones.
324 EXPECT_EQ(nullptr, transport_controller_->GetDtlsTransport(kVideoMid2));
325 EXPECT_EQ(nullptr, transport_controller_->GetRtcpDtlsTransport(kVideoMid2));
326}
327
328TEST_F(JsepTransportControllerTest, SetIceConfig) {
329 CreateJsepTransportController(JsepTransportController::Config());
330 auto description = CreateSessionDescriptionWithoutBundle();
331 EXPECT_TRUE(transport_controller_
332 ->SetLocalDescription(SdpType::kOffer, description.get())
333 .ok());
334
335 transport_controller_->SetIceConfig(
336 CreateIceConfig(kTimeout, cricket::GATHER_CONTINUALLY));
337 FakeDtlsTransport* fake_audio_dtls = static_cast<FakeDtlsTransport*>(
338 transport_controller_->GetDtlsTransport(kAudioMid1));
339 ASSERT_NE(nullptr, fake_audio_dtls);
340 EXPECT_EQ(kTimeout,
341 fake_audio_dtls->fake_ice_transport()->receiving_timeout());
342 EXPECT_TRUE(fake_audio_dtls->fake_ice_transport()->gather_continually());
343
344 // Test that value stored in controller is applied to new transports.
345 AddAudioSection(description.get(), kAudioMid2, kIceUfrag1, kIcePwd1,
346 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
347 nullptr);
348
349 EXPECT_TRUE(transport_controller_
350 ->SetLocalDescription(SdpType::kOffer, description.get())
351 .ok());
352 fake_audio_dtls = static_cast<FakeDtlsTransport*>(
353 transport_controller_->GetDtlsTransport(kAudioMid2));
354 ASSERT_NE(nullptr, fake_audio_dtls);
355 EXPECT_EQ(kTimeout,
356 fake_audio_dtls->fake_ice_transport()->receiving_timeout());
357 EXPECT_TRUE(fake_audio_dtls->fake_ice_transport()->gather_continually());
358}
359
360// Tests the getter and setter of the ICE restart flag.
361TEST_F(JsepTransportControllerTest, NeedIceRestart) {
362 CreateJsepTransportController(JsepTransportController::Config());
363 auto description = CreateSessionDescriptionWithoutBundle();
364 EXPECT_TRUE(transport_controller_
365 ->SetLocalDescription(SdpType::kOffer, description.get())
366 .ok());
367 EXPECT_TRUE(transport_controller_
368 ->SetRemoteDescription(SdpType::kAnswer, description.get())
369 .ok());
370
371 // Initially NeedsIceRestart should return false.
372 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kAudioMid1));
373 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kVideoMid1));
374 // Set the needs-ice-restart flag and verify NeedsIceRestart starts returning
375 // true.
376 transport_controller_->SetNeedsIceRestartFlag();
377 EXPECT_TRUE(transport_controller_->NeedsIceRestart(kAudioMid1));
378 EXPECT_TRUE(transport_controller_->NeedsIceRestart(kVideoMid1));
379 // For a nonexistent transport, false should be returned.
380 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kVideoMid2));
381
382 // Reset the ice_ufrag/ice_pwd for audio.
383 auto audio_transport_info = description->GetTransportInfoByName(kAudioMid1);
384 audio_transport_info->description.ice_ufrag = kIceUfrag2;
385 audio_transport_info->description.ice_pwd = kIcePwd2;
386 EXPECT_TRUE(transport_controller_
387 ->SetLocalDescription(SdpType::kOffer, description.get())
388 .ok());
389 // Because the ICE is only restarted for audio, NeedsIceRestart is expected to
390 // return false for audio and true for video.
391 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kAudioMid1));
392 EXPECT_TRUE(transport_controller_->NeedsIceRestart(kVideoMid1));
393}
394
395TEST_F(JsepTransportControllerTest, MaybeStartGathering) {
396 CreateJsepTransportController(JsepTransportController::Config());
397 auto description = CreateSessionDescriptionWithBundleGroup();
398 EXPECT_TRUE(transport_controller_
399 ->SetLocalDescription(SdpType::kOffer, description.get())
400 .ok());
401 // After setting the local description, we should be able to start gathering
402 // candidates.
403 transport_controller_->MaybeStartGathering();
404 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
405 EXPECT_EQ(1, gathering_state_signal_count_);
406}
407
408TEST_F(JsepTransportControllerTest, AddRemoveRemoteCandidates) {
409 CreateJsepTransportController(JsepTransportController::Config());
410 auto description = CreateSessionDescriptionWithoutBundle();
411 transport_controller_->SetLocalDescription(SdpType::kOffer,
412 description.get());
413 transport_controller_->SetRemoteDescription(SdpType::kAnswer,
414 description.get());
415 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
416 transport_controller_->GetDtlsTransport(kAudioMid1));
417 ASSERT_NE(nullptr, fake_audio_dtls);
418 Candidates candidates;
419 candidates.push_back(
420 CreateCandidate(kAudioMid1, cricket::ICE_CANDIDATE_COMPONENT_RTP));
421 EXPECT_TRUE(
422 transport_controller_->AddRemoteCandidates(kAudioMid1, candidates).ok());
423 EXPECT_EQ(1U,
424 fake_audio_dtls->fake_ice_transport()->remote_candidates().size());
425
426 EXPECT_TRUE(transport_controller_->RemoveRemoteCandidates(candidates).ok());
427 EXPECT_EQ(0U,
428 fake_audio_dtls->fake_ice_transport()->remote_candidates().size());
429}
430
431TEST_F(JsepTransportControllerTest, SetAndGetLocalCertificate) {
432 CreateJsepTransportController(JsepTransportController::Config());
433
434 rtc::scoped_refptr<rtc::RTCCertificate> certificate1 =
435 rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
436 rtc::SSLIdentity::Generate("session1", rtc::KT_DEFAULT)));
437 rtc::scoped_refptr<rtc::RTCCertificate> returned_certificate;
438
439 auto description = rtc::MakeUnique<cricket::SessionDescription>();
440 AddAudioSection(description.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
441 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
442 certificate1);
443
444 // Apply the local certificate.
445 EXPECT_TRUE(transport_controller_->SetLocalCertificate(certificate1));
446 // Apply the local description.
447 EXPECT_TRUE(transport_controller_
448 ->SetLocalDescription(SdpType::kOffer, description.get())
449 .ok());
450 returned_certificate = transport_controller_->GetLocalCertificate(kAudioMid1);
451 EXPECT_TRUE(returned_certificate);
452 EXPECT_EQ(certificate1->identity()->certificate().ToPEMString(),
453 returned_certificate->identity()->certificate().ToPEMString());
454
455 // Should fail if called for a nonexistant transport.
456 EXPECT_EQ(nullptr, transport_controller_->GetLocalCertificate(kVideoMid1));
457
458 // Shouldn't be able to change the identity once set.
459 rtc::scoped_refptr<rtc::RTCCertificate> certificate2 =
460 rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
461 rtc::SSLIdentity::Generate("session2", rtc::KT_DEFAULT)));
462 EXPECT_FALSE(transport_controller_->SetLocalCertificate(certificate2));
463}
464
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800465TEST_F(JsepTransportControllerTest, GetRemoteSSLCertChain) {
Zhi Huange818b6e2018-02-22 15:26:27 -0800466 CreateJsepTransportController(JsepTransportController::Config());
467 auto description = CreateSessionDescriptionWithBundleGroup();
468 EXPECT_TRUE(transport_controller_
469 ->SetLocalDescription(SdpType::kOffer, description.get())
470 .ok());
471 rtc::FakeSSLCertificate fake_certificate("fake_data");
472
473 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
474 transport_controller_->GetDtlsTransport(kAudioMid1));
475 fake_audio_dtls->SetRemoteSSLCertificate(&fake_certificate);
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800476 std::unique_ptr<rtc::SSLCertChain> returned_cert_chain =
477 transport_controller_->GetRemoteSSLCertChain(kAudioMid1);
478 ASSERT_TRUE(returned_cert_chain);
479 ASSERT_EQ(1u, returned_cert_chain->GetSize());
Zhi Huange818b6e2018-02-22 15:26:27 -0800480 EXPECT_EQ(fake_certificate.ToPEMString(),
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800481 returned_cert_chain->Get(0).ToPEMString());
Zhi Huange818b6e2018-02-22 15:26:27 -0800482
483 // Should fail if called for a nonexistant transport.
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800484 EXPECT_FALSE(transport_controller_->GetRemoteSSLCertChain(kAudioMid2));
Zhi Huange818b6e2018-02-22 15:26:27 -0800485}
486
487TEST_F(JsepTransportControllerTest, GetDtlsRole) {
488 CreateJsepTransportController(JsepTransportController::Config());
489 auto offer_certificate =
490 rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
491 rtc::SSLIdentity::Generate("offer", rtc::KT_DEFAULT)));
492 auto answer_certificate =
493 rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
494 rtc::SSLIdentity::Generate("answer", rtc::KT_DEFAULT)));
495 transport_controller_->SetLocalCertificate(offer_certificate);
496
497 auto offer_desc = rtc::MakeUnique<cricket::SessionDescription>();
498 AddAudioSection(offer_desc.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
499 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
500 offer_certificate);
501 auto answer_desc = rtc::MakeUnique<cricket::SessionDescription>();
502 AddAudioSection(answer_desc.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
503 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
504 answer_certificate);
505
506 EXPECT_TRUE(transport_controller_
507 ->SetLocalDescription(SdpType::kOffer, offer_desc.get())
508 .ok());
509
510 rtc::Optional<rtc::SSLRole> role =
511 transport_controller_->GetDtlsRole(kAudioMid1);
512 // The DTLS role is not decided yet.
513 EXPECT_FALSE(role);
514 EXPECT_TRUE(transport_controller_
515 ->SetRemoteDescription(SdpType::kAnswer, answer_desc.get())
516 .ok());
517 role = transport_controller_->GetDtlsRole(kAudioMid1);
518
519 ASSERT_TRUE(role);
520 EXPECT_EQ(rtc::SSL_CLIENT, *role);
521}
522
523TEST_F(JsepTransportControllerTest, GetStats) {
524 CreateJsepTransportController(JsepTransportController::Config());
525 auto description = CreateSessionDescriptionWithBundleGroup();
526 EXPECT_TRUE(transport_controller_
527 ->SetLocalDescription(SdpType::kOffer, description.get())
528 .ok());
529
530 cricket::TransportStats stats;
531 EXPECT_TRUE(transport_controller_->GetStats(kAudioMid1, &stats));
532 EXPECT_EQ(kAudioMid1, stats.transport_name);
533 EXPECT_EQ(1u, stats.channel_stats.size());
534 // Return false for non-existing transport.
535 EXPECT_FALSE(transport_controller_->GetStats(kAudioMid2, &stats));
536}
537
538TEST_F(JsepTransportControllerTest, SignalConnectionStateFailed) {
539 CreateJsepTransportController(JsepTransportController::Config());
540 auto description = CreateSessionDescriptionWithoutBundle();
541 EXPECT_TRUE(transport_controller_
542 ->SetLocalDescription(SdpType::kOffer, description.get())
543 .ok());
544
545 auto fake_ice = static_cast<cricket::FakeIceTransport*>(
546 transport_controller_->GetDtlsTransport(kAudioMid1)->ice_transport());
547 fake_ice->SetCandidatesGatheringComplete();
548 fake_ice->SetConnectionCount(1);
549 // The connection stats will be failed if there is no active connection.
550 fake_ice->SetConnectionCount(0);
551 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
552 EXPECT_EQ(1, connection_state_signal_count_);
553}
554
555TEST_F(JsepTransportControllerTest, SignalConnectionStateConnected) {
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_audio_dtls = static_cast<FakeDtlsTransport*>(
563 transport_controller_->GetDtlsTransport(kAudioMid1));
564 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
565 transport_controller_->GetDtlsTransport(kVideoMid1));
566
567 // First, have one transport connect, and another fail, to ensure that
568 // the first transport connecting didn't trigger a "connected" state signal.
569 // We should only get a signal when all are connected.
570 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
571 fake_audio_dtls->SetWritable(true);
572 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
573 // Decrease the number of the connection to trigger the signal.
574 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
575 fake_video_dtls->fake_ice_transport()->SetConnectionCount(0);
576 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
577
578 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
579 EXPECT_EQ(1, connection_state_signal_count_);
580
581 // Set the connection count to be 2 and the cricket::FakeIceTransport will set
582 // the transport state to be STATE_CONNECTING.
583 fake_video_dtls->fake_ice_transport()->SetConnectionCount(2);
584 fake_video_dtls->SetWritable(true);
585 EXPECT_EQ_WAIT(cricket::kIceConnectionConnected, connection_state_, kTimeout);
586 EXPECT_EQ(2, connection_state_signal_count_);
587}
588
589TEST_F(JsepTransportControllerTest, SignalConnectionStateComplete) {
590 CreateJsepTransportController(JsepTransportController::Config());
591 auto description = CreateSessionDescriptionWithoutBundle();
592 EXPECT_TRUE(transport_controller_
593 ->SetLocalDescription(SdpType::kOffer, description.get())
594 .ok());
595
596 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
597 transport_controller_->GetDtlsTransport(kAudioMid1));
598 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
599 transport_controller_->GetDtlsTransport(kVideoMid1));
600
601 // First, have one transport connect, and another fail, to ensure that
602 // the first transport connecting didn't trigger a "connected" state signal.
603 // We should only get a signal when all are connected.
604 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
605 fake_audio_dtls->SetWritable(true);
606 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
607 // Decrease the number of the connection to trigger the signal.
608 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
609 fake_video_dtls->fake_ice_transport()->SetConnectionCount(0);
610 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
611
612 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
613 EXPECT_EQ(1, connection_state_signal_count_);
614
615 // Set the connection count to be 1 and the cricket::FakeIceTransport will set
616 // the transport state to be STATE_COMPLETED.
617 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
618 fake_video_dtls->SetWritable(true);
619 EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
620 EXPECT_EQ(2, connection_state_signal_count_);
621}
622
623TEST_F(JsepTransportControllerTest, SignalIceGatheringStateGathering) {
624 CreateJsepTransportController(JsepTransportController::Config());
625 auto description = CreateSessionDescriptionWithoutBundle();
626 EXPECT_TRUE(transport_controller_
627 ->SetLocalDescription(SdpType::kOffer, description.get())
628 .ok());
629
630 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
631 transport_controller_->GetDtlsTransport(kAudioMid1));
632 fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
633 // Should be in the gathering state as soon as any transport starts gathering.
634 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
635 EXPECT_EQ(1, gathering_state_signal_count_);
636}
637
638TEST_F(JsepTransportControllerTest, SignalIceGatheringStateComplete) {
639 CreateJsepTransportController(JsepTransportController::Config());
640 auto description = CreateSessionDescriptionWithoutBundle();
641 EXPECT_TRUE(transport_controller_
642 ->SetLocalDescription(SdpType::kOffer, description.get())
643 .ok());
644
645 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
646 transport_controller_->GetDtlsTransport(kAudioMid1));
647 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
648 transport_controller_->GetDtlsTransport(kVideoMid1));
649
650 fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
651 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
652 EXPECT_EQ(1, gathering_state_signal_count_);
653
654 // Have one transport finish gathering, to make sure gathering
655 // completion wasn't signalled if only one transport finished gathering.
656 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
657 EXPECT_EQ(1, gathering_state_signal_count_);
658
659 fake_video_dtls->fake_ice_transport()->MaybeStartGathering();
660 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
661 EXPECT_EQ(1, gathering_state_signal_count_);
662
663 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
664 EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
665 EXPECT_EQ(2, gathering_state_signal_count_);
666}
667
668// Test that when the last transport that hasn't finished connecting and/or
669// gathering is destroyed, the aggregate state jumps to "completed". This can
670// happen if, for example, we have an audio and video transport, the audio
671// transport completes, then we start bundling video on the audio transport.
672TEST_F(JsepTransportControllerTest,
673 SignalingWhenLastIncompleteTransportDestroyed) {
674 CreateJsepTransportController(JsepTransportController::Config());
675 auto description = CreateSessionDescriptionWithBundleGroup();
676 EXPECT_TRUE(transport_controller_
677 ->SetLocalDescription(SdpType::kOffer, description.get())
678 .ok());
679
680 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
681 transport_controller_->GetDtlsTransport(kAudioMid1));
682 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
683 transport_controller_->GetDtlsTransport(kVideoMid1));
684 EXPECT_NE(fake_audio_dtls, fake_video_dtls);
685
686 fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
687 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
688 EXPECT_EQ(1, gathering_state_signal_count_);
689
690 // Let the audio transport complete.
691 fake_audio_dtls->SetWritable(true);
692 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
693 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
694 EXPECT_EQ(1, gathering_state_signal_count_);
695
696 // Set the remote description and enable the bundle.
697 EXPECT_TRUE(transport_controller_
698 ->SetRemoteDescription(SdpType::kAnswer, description.get())
699 .ok());
700 // The BUNDLE should be enabled, the incomplete video transport should be
701 // deleted and the states shoud be updated.
702 fake_video_dtls = static_cast<FakeDtlsTransport*>(
703 transport_controller_->GetDtlsTransport(kVideoMid1));
704 EXPECT_EQ(fake_audio_dtls, fake_video_dtls);
705 EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
706 EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
707 EXPECT_EQ(2, gathering_state_signal_count_);
708}
709
710TEST_F(JsepTransportControllerTest, SignalCandidatesGathered) {
711 CreateJsepTransportController(JsepTransportController::Config());
712 auto description = CreateSessionDescriptionWithBundleGroup();
713 EXPECT_TRUE(transport_controller_
714 ->SetLocalDescription(SdpType::kOffer, description.get())
715 .ok());
716 transport_controller_->MaybeStartGathering();
717
718 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
719 transport_controller_->GetDtlsTransport(kAudioMid1));
720 fake_audio_dtls->fake_ice_transport()->SignalCandidateGathered(
721 fake_audio_dtls->fake_ice_transport(), CreateCandidate(kAudioMid1, 1));
722 EXPECT_EQ_WAIT(1, candidates_signal_count_, kTimeout);
723 EXPECT_EQ(1u, candidates_[kAudioMid1].size());
724}
725
726TEST_F(JsepTransportControllerTest, IceSignalingOccursOnSignalingThread) {
727 network_thread_ = rtc::Thread::CreateWithSocketServer();
728 network_thread_->Start();
729 CreateJsepTransportController(JsepTransportController::Config(),
730 signaling_thread_, network_thread_.get(),
731 /*PortAllocator=*/nullptr);
732 CreateLocalDescriptionAndCompleteConnectionOnNetworkThread();
733
734 // connecting --> connected --> completed
735 EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
736 EXPECT_EQ(2, connection_state_signal_count_);
737
738 // new --> gathering --> complete
739 EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
740 EXPECT_EQ(2, gathering_state_signal_count_);
741
742 EXPECT_EQ_WAIT(1u, candidates_[kAudioMid1].size(), kTimeout);
743 EXPECT_EQ_WAIT(1u, candidates_[kVideoMid1].size(), kTimeout);
744 EXPECT_EQ(2, candidates_signal_count_);
745
746 EXPECT_TRUE(!signaled_on_non_signaling_thread_);
747}
748
749// Older versions of Chrome expect the ICE role to be re-determined when an
750// ICE restart occurs, and also don't perform conflict resolution correctly,
751// so for now we can't safely stop doing this.
752// See: https://bugs.chromium.org/p/chromium/issues/detail?id=628676
753// TODO(deadbeef): Remove this when these old versions of Chrome reach a low
754// enough population.
755TEST_F(JsepTransportControllerTest, IceRoleRedeterminedOnIceRestartByDefault) {
756 CreateJsepTransportController(JsepTransportController::Config());
757 // Let the |transport_controller_| be the controlled side initially.
758 auto remote_offer = rtc::MakeUnique<cricket::SessionDescription>();
759 AddAudioSection(remote_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
760 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
761 nullptr);
762 auto local_answer = rtc::MakeUnique<cricket::SessionDescription>();
763 AddAudioSection(local_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
764 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
765 nullptr);
766
767 EXPECT_TRUE(transport_controller_
768 ->SetRemoteDescription(SdpType::kOffer, remote_offer.get())
769 .ok());
770 EXPECT_TRUE(transport_controller_
771 ->SetLocalDescription(SdpType::kAnswer, local_answer.get())
772 .ok());
773
774 auto fake_dtls = static_cast<FakeDtlsTransport*>(
775 transport_controller_->GetDtlsTransport(kAudioMid1));
776 EXPECT_EQ(cricket::ICEROLE_CONTROLLED,
777 fake_dtls->fake_ice_transport()->GetIceRole());
778
779 // New offer will trigger the ICE restart.
780 auto restart_local_offer = rtc::MakeUnique<cricket::SessionDescription>();
781 AddAudioSection(restart_local_offer.get(), kAudioMid1, kIceUfrag3, kIcePwd3,
782 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
783 nullptr);
784 EXPECT_TRUE(
785 transport_controller_
786 ->SetLocalDescription(SdpType::kOffer, restart_local_offer.get())
787 .ok());
788 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
789 fake_dtls->fake_ice_transport()->GetIceRole());
790}
791
792// Test that if the TransportController was created with the
793// |redetermine_role_on_ice_restart| parameter set to false, the role is *not*
794// redetermined on an ICE restart.
795TEST_F(JsepTransportControllerTest, IceRoleNotRedetermined) {
796 JsepTransportController::Config config;
797 config.redetermine_role_on_ice_restart = false;
798
799 CreateJsepTransportController(config);
800 // Let the |transport_controller_| be the controlled side initially.
801 auto remote_offer = rtc::MakeUnique<cricket::SessionDescription>();
802 AddAudioSection(remote_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
803 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
804 nullptr);
805 auto local_answer = rtc::MakeUnique<cricket::SessionDescription>();
806 AddAudioSection(local_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
807 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
808 nullptr);
809
810 EXPECT_TRUE(transport_controller_
811 ->SetRemoteDescription(SdpType::kOffer, remote_offer.get())
812 .ok());
813 EXPECT_TRUE(transport_controller_
814 ->SetLocalDescription(SdpType::kAnswer, local_answer.get())
815 .ok());
816
817 auto fake_dtls = static_cast<FakeDtlsTransport*>(
818 transport_controller_->GetDtlsTransport(kAudioMid1));
819 EXPECT_EQ(cricket::ICEROLE_CONTROLLED,
820 fake_dtls->fake_ice_transport()->GetIceRole());
821
822 // New offer will trigger the ICE restart.
823 auto restart_local_offer = rtc::MakeUnique<cricket::SessionDescription>();
824 AddAudioSection(restart_local_offer.get(), kAudioMid1, kIceUfrag3, kIcePwd3,
825 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
826 nullptr);
827 EXPECT_TRUE(
828 transport_controller_
829 ->SetLocalDescription(SdpType::kOffer, restart_local_offer.get())
830 .ok());
831 EXPECT_EQ(cricket::ICEROLE_CONTROLLED,
832 fake_dtls->fake_ice_transport()->GetIceRole());
833}
834
835// Tests ICE-Lite mode in remote answer.
836TEST_F(JsepTransportControllerTest, SetIceRoleWhenIceLiteInRemoteAnswer) {
837 CreateJsepTransportController(JsepTransportController::Config());
838 auto local_offer = rtc::MakeUnique<cricket::SessionDescription>();
839 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
840 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
841 nullptr);
842 EXPECT_TRUE(transport_controller_
843 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
844 .ok());
845 auto fake_dtls = static_cast<FakeDtlsTransport*>(
846 transport_controller_->GetDtlsTransport(kAudioMid1));
847 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
848 fake_dtls->fake_ice_transport()->GetIceRole());
849 EXPECT_EQ(cricket::ICEMODE_FULL,
850 fake_dtls->fake_ice_transport()->remote_ice_mode());
851
852 auto remote_answer = rtc::MakeUnique<cricket::SessionDescription>();
853 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
854 cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_PASSIVE,
855 nullptr);
856 EXPECT_TRUE(transport_controller_
857 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
858 .ok());
859 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
860 fake_dtls->fake_ice_transport()->GetIceRole());
861 EXPECT_EQ(cricket::ICEMODE_LITE,
862 fake_dtls->fake_ice_transport()->remote_ice_mode());
863}
864
865// Tests that the ICE role remains "controlling" if a subsequent offer that
866// does an ICE restart is received from an ICE lite endpoint. Regression test
867// for: https://crbug.com/710760
868TEST_F(JsepTransportControllerTest,
869 IceRoleIsControllingAfterIceRestartFromIceLiteEndpoint) {
870 CreateJsepTransportController(JsepTransportController::Config());
871 auto remote_offer = rtc::MakeUnique<cricket::SessionDescription>();
872 AddAudioSection(remote_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
873 cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_ACTPASS,
874 nullptr);
875 auto local_answer = rtc::MakeUnique<cricket::SessionDescription>();
876 AddAudioSection(local_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
877 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
878 nullptr);
879 // Initial Offer/Answer exchange. If the remote offerer is ICE-Lite, then the
880 // local side is the controlling.
881 EXPECT_TRUE(transport_controller_
882 ->SetRemoteDescription(SdpType::kOffer, remote_offer.get())
883 .ok());
884 EXPECT_TRUE(transport_controller_
885 ->SetLocalDescription(SdpType::kAnswer, local_answer.get())
886 .ok());
887 auto fake_dtls = static_cast<FakeDtlsTransport*>(
888 transport_controller_->GetDtlsTransport(kAudioMid1));
889 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
890 fake_dtls->fake_ice_transport()->GetIceRole());
891
892 // In the subsequence remote offer triggers an ICE restart.
893 auto remote_offer2 = rtc::MakeUnique<cricket::SessionDescription>();
894 AddAudioSection(remote_offer2.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
895 cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_ACTPASS,
896 nullptr);
897 auto local_answer2 = rtc::MakeUnique<cricket::SessionDescription>();
898 AddAudioSection(local_answer2.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
899 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
900 nullptr);
901 EXPECT_TRUE(transport_controller_
902 ->SetRemoteDescription(SdpType::kOffer, remote_offer2.get())
903 .ok());
904 EXPECT_TRUE(transport_controller_
905 ->SetLocalDescription(SdpType::kAnswer, local_answer2.get())
906 .ok());
907 fake_dtls = static_cast<FakeDtlsTransport*>(
908 transport_controller_->GetDtlsTransport(kAudioMid1));
909 // The local side is still the controlling role since the remote side is using
910 // ICE-Lite.
911 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
912 fake_dtls->fake_ice_transport()->GetIceRole());
913}
914
915// Tests that the SDP has more than one audio/video m= sections.
916TEST_F(JsepTransportControllerTest, MultipleMediaSectionsOfSameTypeWithBundle) {
917 CreateJsepTransportController(JsepTransportController::Config());
918 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
919 bundle_group.AddContentName(kAudioMid1);
920 bundle_group.AddContentName(kAudioMid2);
921 bundle_group.AddContentName(kVideoMid1);
922 bundle_group.AddContentName(kDataMid1);
923
924 auto local_offer = rtc::MakeUnique<cricket::SessionDescription>();
925 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
926 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
927 nullptr);
928 AddAudioSection(local_offer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
929 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
930 nullptr);
931 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
932 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
933 nullptr);
934 AddDataSection(local_offer.get(), kDataMid1,
935 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
936 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
937 nullptr);
938
939 auto remote_answer = rtc::MakeUnique<cricket::SessionDescription>();
940 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
941 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
942 nullptr);
943 AddAudioSection(remote_answer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
944 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
945 nullptr);
946 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
947 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
948 nullptr);
949 AddDataSection(remote_answer.get(), kDataMid1,
950 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
951 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
952 nullptr);
953
954 local_offer->AddGroup(bundle_group);
955 remote_answer->AddGroup(bundle_group);
956
957 EXPECT_TRUE(transport_controller_
958 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
959 .ok());
960 EXPECT_TRUE(transport_controller_
961 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
962 .ok());
963 // Verify that all the sections are bundled on kAudio1.
964 auto transport1 = transport_controller_->GetRtpTransport(kAudioMid1);
965 auto transport2 = transport_controller_->GetRtpTransport(kAudioMid2);
966 auto transport3 = transport_controller_->GetRtpTransport(kVideoMid1);
967 auto transport4 = transport_controller_->GetRtpTransport(kDataMid1);
968 EXPECT_EQ(transport1, transport2);
969 EXPECT_EQ(transport1, transport3);
970 EXPECT_EQ(transport1, transport4);
971
972 // Verify the OnRtpTransport/DtlsTransportChanged signals are fired correctly.
973 auto it = changed_rtp_transport_by_mid_.find(kAudioMid2);
974 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
975 EXPECT_EQ(transport1, it->second);
976 it = changed_rtp_transport_by_mid_.find(kAudioMid2);
977 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
978 EXPECT_EQ(transport1, it->second);
979 it = changed_rtp_transport_by_mid_.find(kVideoMid1);
980 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
981 EXPECT_EQ(transport1, it->second);
982 // Verify the DtlsTransport for the SCTP data channel is reset correctly.
983 auto it2 = changed_dtls_transport_by_mid_.find(kDataMid1);
984 ASSERT_TRUE(it2 != changed_dtls_transport_by_mid_.end());
985 EXPECT_EQ(transport1->rtp_packet_transport(), it2->second);
986}
987
988// Tests that only a subset of all the m= sections are bundled.
989TEST_F(JsepTransportControllerTest, BundleSubsetOfMediaSections) {
990 CreateJsepTransportController(JsepTransportController::Config());
991 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
992 bundle_group.AddContentName(kAudioMid1);
993 bundle_group.AddContentName(kVideoMid1);
994
995 auto local_offer = rtc::MakeUnique<cricket::SessionDescription>();
996 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
997 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
998 nullptr);
999 AddAudioSection(local_offer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
1000 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1001 nullptr);
1002 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1003 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1004 nullptr);
1005
1006 auto remote_answer = rtc::MakeUnique<cricket::SessionDescription>();
1007 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1008 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1009 nullptr);
1010 AddAudioSection(remote_answer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
1011 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1012 nullptr);
1013 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1014 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1015 nullptr);
1016
1017 local_offer->AddGroup(bundle_group);
1018 remote_answer->AddGroup(bundle_group);
1019 EXPECT_TRUE(transport_controller_
1020 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1021 .ok());
1022 EXPECT_TRUE(transport_controller_
1023 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1024 .ok());
1025
1026 // Verifiy that only |kAudio1| and |kVideo1| are bundled.
1027 auto transport1 = transport_controller_->GetRtpTransport(kAudioMid1);
1028 auto transport2 = transport_controller_->GetRtpTransport(kAudioMid2);
1029 auto transport3 = transport_controller_->GetRtpTransport(kVideoMid1);
1030 EXPECT_NE(transport1, transport2);
1031 EXPECT_EQ(transport1, transport3);
1032
1033 auto it = changed_rtp_transport_by_mid_.find(kVideoMid1);
1034 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1035 EXPECT_EQ(transport1, it->second);
1036 it = changed_rtp_transport_by_mid_.find(kAudioMid2);
1037 EXPECT_TRUE(it == changed_rtp_transport_by_mid_.end());
1038}
1039
1040// Tests that the initial offer/answer only have data section and audio/video
1041// sections are added in the subsequent offer.
1042TEST_F(JsepTransportControllerTest, BundleOnDataSectionInSubsequentOffer) {
1043 CreateJsepTransportController(JsepTransportController::Config());
1044 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1045 bundle_group.AddContentName(kDataMid1);
1046
1047 auto local_offer = rtc::MakeUnique<cricket::SessionDescription>();
1048 AddDataSection(local_offer.get(), kDataMid1,
1049 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
1050 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1051 nullptr);
1052 auto remote_answer = rtc::MakeUnique<cricket::SessionDescription>();
1053 AddDataSection(remote_answer.get(), kDataMid1,
1054 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
1055 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1056 nullptr);
1057 local_offer->AddGroup(bundle_group);
1058 remote_answer->AddGroup(bundle_group);
1059
1060 EXPECT_TRUE(transport_controller_
1061 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1062 .ok());
1063 EXPECT_TRUE(transport_controller_
1064 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1065 .ok());
1066 auto data_transport = transport_controller_->GetRtpTransport(kDataMid1);
1067
1068 // Add audio/video sections in subsequent offer.
1069 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1070 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1071 nullptr);
1072 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1073 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1074 nullptr);
1075 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1076 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1077 nullptr);
1078 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1079 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1080 nullptr);
1081
1082 // Reset the bundle group and do another offer/answer exchange.
1083 bundle_group.AddContentName(kAudioMid1);
1084 bundle_group.AddContentName(kVideoMid1);
1085 local_offer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1086 remote_answer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1087 local_offer->AddGroup(bundle_group);
1088 remote_answer->AddGroup(bundle_group);
1089
1090 EXPECT_TRUE(transport_controller_
1091 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1092 .ok());
1093 EXPECT_TRUE(transport_controller_
1094 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1095 .ok());
1096
1097 auto audio_transport = transport_controller_->GetRtpTransport(kAudioMid1);
1098 auto video_transport = transport_controller_->GetRtpTransport(kVideoMid1);
1099 EXPECT_EQ(data_transport, audio_transport);
1100 EXPECT_EQ(data_transport, video_transport);
1101}
1102
1103TEST_F(JsepTransportControllerTest, VideoDataRejectedInAnswer) {
1104 CreateJsepTransportController(JsepTransportController::Config());
1105 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1106 bundle_group.AddContentName(kAudioMid1);
1107 bundle_group.AddContentName(kVideoMid1);
1108 bundle_group.AddContentName(kDataMid1);
1109
1110 auto local_offer = rtc::MakeUnique<cricket::SessionDescription>();
1111 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1112 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1113 nullptr);
1114 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1115 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1116 nullptr);
1117 AddDataSection(local_offer.get(), kDataMid1,
1118 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1119 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1120 nullptr);
1121
1122 auto remote_answer = rtc::MakeUnique<cricket::SessionDescription>();
1123 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1124 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1125 nullptr);
1126 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1127 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1128 nullptr);
1129 AddDataSection(remote_answer.get(), kDataMid1,
1130 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1131 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1132 nullptr);
1133 // Reject video and data section.
1134 remote_answer->contents()[1].rejected = true;
1135 remote_answer->contents()[2].rejected = true;
1136
1137 local_offer->AddGroup(bundle_group);
1138 remote_answer->AddGroup(bundle_group);
1139
1140 EXPECT_TRUE(transport_controller_
1141 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1142 .ok());
1143 EXPECT_TRUE(transport_controller_
1144 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1145 .ok());
1146
1147 // Verify the RtpTransport/DtlsTransport is destroyed correctly.
1148 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kVideoMid1));
1149 EXPECT_EQ(nullptr, transport_controller_->GetDtlsTransport(kDataMid1));
1150 // Verify the signals are fired correctly.
1151 auto it = changed_rtp_transport_by_mid_.find(kVideoMid1);
1152 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1153 EXPECT_EQ(nullptr, it->second);
1154 auto it2 = changed_dtls_transport_by_mid_.find(kDataMid1);
1155 ASSERT_TRUE(it2 != changed_dtls_transport_by_mid_.end());
1156 EXPECT_EQ(nullptr, it2->second);
1157}
1158
1159// Tests that changing the bundled MID in subsequent offer/answer exchange is
1160// not supported.
1161// TODO(bugs.webrtc.org/6704): Change this test to expect success once issue is
1162// fixed
1163TEST_F(JsepTransportControllerTest, ChangeBundledMidNotSupported) {
1164 CreateJsepTransportController(JsepTransportController::Config());
1165 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1166 bundle_group.AddContentName(kAudioMid1);
1167 bundle_group.AddContentName(kVideoMid1);
1168
1169 auto local_offer = rtc::MakeUnique<cricket::SessionDescription>();
1170 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1171 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1172 nullptr);
1173 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1174 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1175 nullptr);
1176
1177 auto remote_answer = rtc::MakeUnique<cricket::SessionDescription>();
1178 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1179 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1180 nullptr);
1181 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1182 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1183 nullptr);
1184
1185 local_offer->AddGroup(bundle_group);
1186 remote_answer->AddGroup(bundle_group);
1187 EXPECT_TRUE(transport_controller_
1188 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1189 .ok());
1190 EXPECT_TRUE(transport_controller_
1191 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1192 .ok());
1193 EXPECT_EQ(transport_controller_->GetRtpTransport(kAudioMid1),
1194 transport_controller_->GetRtpTransport(kVideoMid1));
1195
1196 // Reorder the bundle group.
1197 EXPECT_TRUE(bundle_group.RemoveContentName(kAudioMid1));
1198 bundle_group.AddContentName(kAudioMid1);
1199 // The answerer uses the new bundle group and now the bundle mid is changed to
1200 // |kVideo1|.
1201 remote_answer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1202 remote_answer->AddGroup(bundle_group);
1203 EXPECT_TRUE(transport_controller_
1204 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1205 .ok());
1206 EXPECT_FALSE(transport_controller_
1207 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1208 .ok());
1209}
1210
1211} // namespace webrtc