blob: 887c4beacf3fa36d1d7d2272fc45c79e65801d65 [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
465TEST_F(JsepTransportControllerTest, GetRemoteSSLCertificate) {
466 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);
476 std::unique_ptr<rtc::SSLCertificate> returned_certificate =
477 transport_controller_->GetRemoteSSLCertificate(kAudioMid1);
478 EXPECT_TRUE(returned_certificate);
479 EXPECT_EQ(fake_certificate.ToPEMString(),
480 returned_certificate->ToPEMString());
481
482 // Should fail if called for a nonexistant transport.
483 EXPECT_FALSE(transport_controller_->GetRemoteSSLCertificate(kAudioMid2));
484}
485
486TEST_F(JsepTransportControllerTest, GetDtlsRole) {
487 CreateJsepTransportController(JsepTransportController::Config());
488 auto offer_certificate =
489 rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
490 rtc::SSLIdentity::Generate("offer", rtc::KT_DEFAULT)));
491 auto answer_certificate =
492 rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
493 rtc::SSLIdentity::Generate("answer", rtc::KT_DEFAULT)));
494 transport_controller_->SetLocalCertificate(offer_certificate);
495
496 auto offer_desc = rtc::MakeUnique<cricket::SessionDescription>();
497 AddAudioSection(offer_desc.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
498 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
499 offer_certificate);
500 auto answer_desc = rtc::MakeUnique<cricket::SessionDescription>();
501 AddAudioSection(answer_desc.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
502 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
503 answer_certificate);
504
505 EXPECT_TRUE(transport_controller_
506 ->SetLocalDescription(SdpType::kOffer, offer_desc.get())
507 .ok());
508
509 rtc::Optional<rtc::SSLRole> role =
510 transport_controller_->GetDtlsRole(kAudioMid1);
511 // The DTLS role is not decided yet.
512 EXPECT_FALSE(role);
513 EXPECT_TRUE(transport_controller_
514 ->SetRemoteDescription(SdpType::kAnswer, answer_desc.get())
515 .ok());
516 role = transport_controller_->GetDtlsRole(kAudioMid1);
517
518 ASSERT_TRUE(role);
519 EXPECT_EQ(rtc::SSL_CLIENT, *role);
520}
521
522TEST_F(JsepTransportControllerTest, GetStats) {
523 CreateJsepTransportController(JsepTransportController::Config());
524 auto description = CreateSessionDescriptionWithBundleGroup();
525 EXPECT_TRUE(transport_controller_
526 ->SetLocalDescription(SdpType::kOffer, description.get())
527 .ok());
528
529 cricket::TransportStats stats;
530 EXPECT_TRUE(transport_controller_->GetStats(kAudioMid1, &stats));
531 EXPECT_EQ(kAudioMid1, stats.transport_name);
532 EXPECT_EQ(1u, stats.channel_stats.size());
533 // Return false for non-existing transport.
534 EXPECT_FALSE(transport_controller_->GetStats(kAudioMid2, &stats));
535}
536
537TEST_F(JsepTransportControllerTest, SignalConnectionStateFailed) {
538 CreateJsepTransportController(JsepTransportController::Config());
539 auto description = CreateSessionDescriptionWithoutBundle();
540 EXPECT_TRUE(transport_controller_
541 ->SetLocalDescription(SdpType::kOffer, description.get())
542 .ok());
543
544 auto fake_ice = static_cast<cricket::FakeIceTransport*>(
545 transport_controller_->GetDtlsTransport(kAudioMid1)->ice_transport());
546 fake_ice->SetCandidatesGatheringComplete();
547 fake_ice->SetConnectionCount(1);
548 // The connection stats will be failed if there is no active connection.
549 fake_ice->SetConnectionCount(0);
550 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
551 EXPECT_EQ(1, connection_state_signal_count_);
552}
553
554TEST_F(JsepTransportControllerTest, SignalConnectionStateConnected) {
555 CreateJsepTransportController(JsepTransportController::Config());
556 auto description = CreateSessionDescriptionWithoutBundle();
557 EXPECT_TRUE(transport_controller_
558 ->SetLocalDescription(SdpType::kOffer, description.get())
559 .ok());
560
561 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
562 transport_controller_->GetDtlsTransport(kAudioMid1));
563 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
564 transport_controller_->GetDtlsTransport(kVideoMid1));
565
566 // First, have one transport connect, and another fail, to ensure that
567 // the first transport connecting didn't trigger a "connected" state signal.
568 // We should only get a signal when all are connected.
569 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
570 fake_audio_dtls->SetWritable(true);
571 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
572 // Decrease the number of the connection to trigger the signal.
573 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
574 fake_video_dtls->fake_ice_transport()->SetConnectionCount(0);
575 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
576
577 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
578 EXPECT_EQ(1, connection_state_signal_count_);
579
580 // Set the connection count to be 2 and the cricket::FakeIceTransport will set
581 // the transport state to be STATE_CONNECTING.
582 fake_video_dtls->fake_ice_transport()->SetConnectionCount(2);
583 fake_video_dtls->SetWritable(true);
584 EXPECT_EQ_WAIT(cricket::kIceConnectionConnected, connection_state_, kTimeout);
585 EXPECT_EQ(2, connection_state_signal_count_);
586}
587
588TEST_F(JsepTransportControllerTest, SignalConnectionStateComplete) {
589 CreateJsepTransportController(JsepTransportController::Config());
590 auto description = CreateSessionDescriptionWithoutBundle();
591 EXPECT_TRUE(transport_controller_
592 ->SetLocalDescription(SdpType::kOffer, description.get())
593 .ok());
594
595 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
596 transport_controller_->GetDtlsTransport(kAudioMid1));
597 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
598 transport_controller_->GetDtlsTransport(kVideoMid1));
599
600 // First, have one transport connect, and another fail, to ensure that
601 // the first transport connecting didn't trigger a "connected" state signal.
602 // We should only get a signal when all are connected.
603 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
604 fake_audio_dtls->SetWritable(true);
605 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
606 // Decrease the number of the connection to trigger the signal.
607 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
608 fake_video_dtls->fake_ice_transport()->SetConnectionCount(0);
609 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
610
611 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
612 EXPECT_EQ(1, connection_state_signal_count_);
613
614 // Set the connection count to be 1 and the cricket::FakeIceTransport will set
615 // the transport state to be STATE_COMPLETED.
616 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
617 fake_video_dtls->SetWritable(true);
618 EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
619 EXPECT_EQ(2, connection_state_signal_count_);
620}
621
622TEST_F(JsepTransportControllerTest, SignalIceGatheringStateGathering) {
623 CreateJsepTransportController(JsepTransportController::Config());
624 auto description = CreateSessionDescriptionWithoutBundle();
625 EXPECT_TRUE(transport_controller_
626 ->SetLocalDescription(SdpType::kOffer, description.get())
627 .ok());
628
629 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
630 transport_controller_->GetDtlsTransport(kAudioMid1));
631 fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
632 // Should be in the gathering state as soon as any transport starts gathering.
633 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
634 EXPECT_EQ(1, gathering_state_signal_count_);
635}
636
637TEST_F(JsepTransportControllerTest, SignalIceGatheringStateComplete) {
638 CreateJsepTransportController(JsepTransportController::Config());
639 auto description = CreateSessionDescriptionWithoutBundle();
640 EXPECT_TRUE(transport_controller_
641 ->SetLocalDescription(SdpType::kOffer, description.get())
642 .ok());
643
644 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
645 transport_controller_->GetDtlsTransport(kAudioMid1));
646 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
647 transport_controller_->GetDtlsTransport(kVideoMid1));
648
649 fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
650 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
651 EXPECT_EQ(1, gathering_state_signal_count_);
652
653 // Have one transport finish gathering, to make sure gathering
654 // completion wasn't signalled if only one transport finished gathering.
655 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
656 EXPECT_EQ(1, gathering_state_signal_count_);
657
658 fake_video_dtls->fake_ice_transport()->MaybeStartGathering();
659 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
660 EXPECT_EQ(1, gathering_state_signal_count_);
661
662 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
663 EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
664 EXPECT_EQ(2, gathering_state_signal_count_);
665}
666
667// Test that when the last transport that hasn't finished connecting and/or
668// gathering is destroyed, the aggregate state jumps to "completed". This can
669// happen if, for example, we have an audio and video transport, the audio
670// transport completes, then we start bundling video on the audio transport.
671TEST_F(JsepTransportControllerTest,
672 SignalingWhenLastIncompleteTransportDestroyed) {
673 CreateJsepTransportController(JsepTransportController::Config());
674 auto description = CreateSessionDescriptionWithBundleGroup();
675 EXPECT_TRUE(transport_controller_
676 ->SetLocalDescription(SdpType::kOffer, description.get())
677 .ok());
678
679 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
680 transport_controller_->GetDtlsTransport(kAudioMid1));
681 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
682 transport_controller_->GetDtlsTransport(kVideoMid1));
683 EXPECT_NE(fake_audio_dtls, fake_video_dtls);
684
685 fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
686 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
687 EXPECT_EQ(1, gathering_state_signal_count_);
688
689 // Let the audio transport complete.
690 fake_audio_dtls->SetWritable(true);
691 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
692 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
693 EXPECT_EQ(1, gathering_state_signal_count_);
694
695 // Set the remote description and enable the bundle.
696 EXPECT_TRUE(transport_controller_
697 ->SetRemoteDescription(SdpType::kAnswer, description.get())
698 .ok());
699 // The BUNDLE should be enabled, the incomplete video transport should be
700 // deleted and the states shoud be updated.
701 fake_video_dtls = static_cast<FakeDtlsTransport*>(
702 transport_controller_->GetDtlsTransport(kVideoMid1));
703 EXPECT_EQ(fake_audio_dtls, fake_video_dtls);
704 EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
705 EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
706 EXPECT_EQ(2, gathering_state_signal_count_);
707}
708
709TEST_F(JsepTransportControllerTest, SignalCandidatesGathered) {
710 CreateJsepTransportController(JsepTransportController::Config());
711 auto description = CreateSessionDescriptionWithBundleGroup();
712 EXPECT_TRUE(transport_controller_
713 ->SetLocalDescription(SdpType::kOffer, description.get())
714 .ok());
715 transport_controller_->MaybeStartGathering();
716
717 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
718 transport_controller_->GetDtlsTransport(kAudioMid1));
719 fake_audio_dtls->fake_ice_transport()->SignalCandidateGathered(
720 fake_audio_dtls->fake_ice_transport(), CreateCandidate(kAudioMid1, 1));
721 EXPECT_EQ_WAIT(1, candidates_signal_count_, kTimeout);
722 EXPECT_EQ(1u, candidates_[kAudioMid1].size());
723}
724
725TEST_F(JsepTransportControllerTest, IceSignalingOccursOnSignalingThread) {
726 network_thread_ = rtc::Thread::CreateWithSocketServer();
727 network_thread_->Start();
728 CreateJsepTransportController(JsepTransportController::Config(),
729 signaling_thread_, network_thread_.get(),
730 /*PortAllocator=*/nullptr);
731 CreateLocalDescriptionAndCompleteConnectionOnNetworkThread();
732
733 // connecting --> connected --> completed
734 EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
735 EXPECT_EQ(2, connection_state_signal_count_);
736
737 // new --> gathering --> complete
738 EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
739 EXPECT_EQ(2, gathering_state_signal_count_);
740
741 EXPECT_EQ_WAIT(1u, candidates_[kAudioMid1].size(), kTimeout);
742 EXPECT_EQ_WAIT(1u, candidates_[kVideoMid1].size(), kTimeout);
743 EXPECT_EQ(2, candidates_signal_count_);
744
745 EXPECT_TRUE(!signaled_on_non_signaling_thread_);
746}
747
748// Older versions of Chrome expect the ICE role to be re-determined when an
749// ICE restart occurs, and also don't perform conflict resolution correctly,
750// so for now we can't safely stop doing this.
751// See: https://bugs.chromium.org/p/chromium/issues/detail?id=628676
752// TODO(deadbeef): Remove this when these old versions of Chrome reach a low
753// enough population.
754TEST_F(JsepTransportControllerTest, IceRoleRedeterminedOnIceRestartByDefault) {
755 CreateJsepTransportController(JsepTransportController::Config());
756 // Let the |transport_controller_| be the controlled side initially.
757 auto remote_offer = rtc::MakeUnique<cricket::SessionDescription>();
758 AddAudioSection(remote_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
759 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
760 nullptr);
761 auto local_answer = rtc::MakeUnique<cricket::SessionDescription>();
762 AddAudioSection(local_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
763 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
764 nullptr);
765
766 EXPECT_TRUE(transport_controller_
767 ->SetRemoteDescription(SdpType::kOffer, remote_offer.get())
768 .ok());
769 EXPECT_TRUE(transport_controller_
770 ->SetLocalDescription(SdpType::kAnswer, local_answer.get())
771 .ok());
772
773 auto fake_dtls = static_cast<FakeDtlsTransport*>(
774 transport_controller_->GetDtlsTransport(kAudioMid1));
775 EXPECT_EQ(cricket::ICEROLE_CONTROLLED,
776 fake_dtls->fake_ice_transport()->GetIceRole());
777
778 // New offer will trigger the ICE restart.
779 auto restart_local_offer = rtc::MakeUnique<cricket::SessionDescription>();
780 AddAudioSection(restart_local_offer.get(), kAudioMid1, kIceUfrag3, kIcePwd3,
781 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
782 nullptr);
783 EXPECT_TRUE(
784 transport_controller_
785 ->SetLocalDescription(SdpType::kOffer, restart_local_offer.get())
786 .ok());
787 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
788 fake_dtls->fake_ice_transport()->GetIceRole());
789}
790
791// Test that if the TransportController was created with the
792// |redetermine_role_on_ice_restart| parameter set to false, the role is *not*
793// redetermined on an ICE restart.
794TEST_F(JsepTransportControllerTest, IceRoleNotRedetermined) {
795 JsepTransportController::Config config;
796 config.redetermine_role_on_ice_restart = false;
797
798 CreateJsepTransportController(config);
799 // Let the |transport_controller_| be the controlled side initially.
800 auto remote_offer = rtc::MakeUnique<cricket::SessionDescription>();
801 AddAudioSection(remote_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
802 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
803 nullptr);
804 auto local_answer = rtc::MakeUnique<cricket::SessionDescription>();
805 AddAudioSection(local_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
806 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
807 nullptr);
808
809 EXPECT_TRUE(transport_controller_
810 ->SetRemoteDescription(SdpType::kOffer, remote_offer.get())
811 .ok());
812 EXPECT_TRUE(transport_controller_
813 ->SetLocalDescription(SdpType::kAnswer, local_answer.get())
814 .ok());
815
816 auto fake_dtls = static_cast<FakeDtlsTransport*>(
817 transport_controller_->GetDtlsTransport(kAudioMid1));
818 EXPECT_EQ(cricket::ICEROLE_CONTROLLED,
819 fake_dtls->fake_ice_transport()->GetIceRole());
820
821 // New offer will trigger the ICE restart.
822 auto restart_local_offer = rtc::MakeUnique<cricket::SessionDescription>();
823 AddAudioSection(restart_local_offer.get(), kAudioMid1, kIceUfrag3, kIcePwd3,
824 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
825 nullptr);
826 EXPECT_TRUE(
827 transport_controller_
828 ->SetLocalDescription(SdpType::kOffer, restart_local_offer.get())
829 .ok());
830 EXPECT_EQ(cricket::ICEROLE_CONTROLLED,
831 fake_dtls->fake_ice_transport()->GetIceRole());
832}
833
834// Tests ICE-Lite mode in remote answer.
835TEST_F(JsepTransportControllerTest, SetIceRoleWhenIceLiteInRemoteAnswer) {
836 CreateJsepTransportController(JsepTransportController::Config());
837 auto local_offer = rtc::MakeUnique<cricket::SessionDescription>();
838 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
839 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
840 nullptr);
841 EXPECT_TRUE(transport_controller_
842 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
843 .ok());
844 auto fake_dtls = static_cast<FakeDtlsTransport*>(
845 transport_controller_->GetDtlsTransport(kAudioMid1));
846 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
847 fake_dtls->fake_ice_transport()->GetIceRole());
848 EXPECT_EQ(cricket::ICEMODE_FULL,
849 fake_dtls->fake_ice_transport()->remote_ice_mode());
850
851 auto remote_answer = rtc::MakeUnique<cricket::SessionDescription>();
852 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
853 cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_PASSIVE,
854 nullptr);
855 EXPECT_TRUE(transport_controller_
856 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
857 .ok());
858 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
859 fake_dtls->fake_ice_transport()->GetIceRole());
860 EXPECT_EQ(cricket::ICEMODE_LITE,
861 fake_dtls->fake_ice_transport()->remote_ice_mode());
862}
863
864// Tests that the ICE role remains "controlling" if a subsequent offer that
865// does an ICE restart is received from an ICE lite endpoint. Regression test
866// for: https://crbug.com/710760
867TEST_F(JsepTransportControllerTest,
868 IceRoleIsControllingAfterIceRestartFromIceLiteEndpoint) {
869 CreateJsepTransportController(JsepTransportController::Config());
870 auto remote_offer = rtc::MakeUnique<cricket::SessionDescription>();
871 AddAudioSection(remote_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
872 cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_ACTPASS,
873 nullptr);
874 auto local_answer = rtc::MakeUnique<cricket::SessionDescription>();
875 AddAudioSection(local_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
876 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
877 nullptr);
878 // Initial Offer/Answer exchange. If the remote offerer is ICE-Lite, then the
879 // local side is the controlling.
880 EXPECT_TRUE(transport_controller_
881 ->SetRemoteDescription(SdpType::kOffer, remote_offer.get())
882 .ok());
883 EXPECT_TRUE(transport_controller_
884 ->SetLocalDescription(SdpType::kAnswer, local_answer.get())
885 .ok());
886 auto fake_dtls = static_cast<FakeDtlsTransport*>(
887 transport_controller_->GetDtlsTransport(kAudioMid1));
888 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
889 fake_dtls->fake_ice_transport()->GetIceRole());
890
891 // In the subsequence remote offer triggers an ICE restart.
892 auto remote_offer2 = rtc::MakeUnique<cricket::SessionDescription>();
893 AddAudioSection(remote_offer2.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
894 cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_ACTPASS,
895 nullptr);
896 auto local_answer2 = rtc::MakeUnique<cricket::SessionDescription>();
897 AddAudioSection(local_answer2.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
898 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
899 nullptr);
900 EXPECT_TRUE(transport_controller_
901 ->SetRemoteDescription(SdpType::kOffer, remote_offer2.get())
902 .ok());
903 EXPECT_TRUE(transport_controller_
904 ->SetLocalDescription(SdpType::kAnswer, local_answer2.get())
905 .ok());
906 fake_dtls = static_cast<FakeDtlsTransport*>(
907 transport_controller_->GetDtlsTransport(kAudioMid1));
908 // The local side is still the controlling role since the remote side is using
909 // ICE-Lite.
910 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
911 fake_dtls->fake_ice_transport()->GetIceRole());
912}
913
914// Tests that the SDP has more than one audio/video m= sections.
915TEST_F(JsepTransportControllerTest, MultipleMediaSectionsOfSameTypeWithBundle) {
916 CreateJsepTransportController(JsepTransportController::Config());
917 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
918 bundle_group.AddContentName(kAudioMid1);
919 bundle_group.AddContentName(kAudioMid2);
920 bundle_group.AddContentName(kVideoMid1);
921 bundle_group.AddContentName(kDataMid1);
922
923 auto local_offer = rtc::MakeUnique<cricket::SessionDescription>();
924 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
925 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
926 nullptr);
927 AddAudioSection(local_offer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
928 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
929 nullptr);
930 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
931 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
932 nullptr);
933 AddDataSection(local_offer.get(), kDataMid1,
934 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
935 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
936 nullptr);
937
938 auto remote_answer = rtc::MakeUnique<cricket::SessionDescription>();
939 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
940 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
941 nullptr);
942 AddAudioSection(remote_answer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
943 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
944 nullptr);
945 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
946 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
947 nullptr);
948 AddDataSection(remote_answer.get(), kDataMid1,
949 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
950 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
951 nullptr);
952
953 local_offer->AddGroup(bundle_group);
954 remote_answer->AddGroup(bundle_group);
955
956 EXPECT_TRUE(transport_controller_
957 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
958 .ok());
959 EXPECT_TRUE(transport_controller_
960 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
961 .ok());
962 // Verify that all the sections are bundled on kAudio1.
963 auto transport1 = transport_controller_->GetRtpTransport(kAudioMid1);
964 auto transport2 = transport_controller_->GetRtpTransport(kAudioMid2);
965 auto transport3 = transport_controller_->GetRtpTransport(kVideoMid1);
966 auto transport4 = transport_controller_->GetRtpTransport(kDataMid1);
967 EXPECT_EQ(transport1, transport2);
968 EXPECT_EQ(transport1, transport3);
969 EXPECT_EQ(transport1, transport4);
970
971 // Verify the OnRtpTransport/DtlsTransportChanged signals are fired correctly.
972 auto it = changed_rtp_transport_by_mid_.find(kAudioMid2);
973 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
974 EXPECT_EQ(transport1, it->second);
975 it = changed_rtp_transport_by_mid_.find(kAudioMid2);
976 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
977 EXPECT_EQ(transport1, it->second);
978 it = changed_rtp_transport_by_mid_.find(kVideoMid1);
979 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
980 EXPECT_EQ(transport1, it->second);
981 // Verify the DtlsTransport for the SCTP data channel is reset correctly.
982 auto it2 = changed_dtls_transport_by_mid_.find(kDataMid1);
983 ASSERT_TRUE(it2 != changed_dtls_transport_by_mid_.end());
984 EXPECT_EQ(transport1->rtp_packet_transport(), it2->second);
985}
986
987// Tests that only a subset of all the m= sections are bundled.
988TEST_F(JsepTransportControllerTest, BundleSubsetOfMediaSections) {
989 CreateJsepTransportController(JsepTransportController::Config());
990 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
991 bundle_group.AddContentName(kAudioMid1);
992 bundle_group.AddContentName(kVideoMid1);
993
994 auto local_offer = rtc::MakeUnique<cricket::SessionDescription>();
995 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
996 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
997 nullptr);
998 AddAudioSection(local_offer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
999 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1000 nullptr);
1001 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1002 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1003 nullptr);
1004
1005 auto remote_answer = rtc::MakeUnique<cricket::SessionDescription>();
1006 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1007 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1008 nullptr);
1009 AddAudioSection(remote_answer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
1010 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1011 nullptr);
1012 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1013 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1014 nullptr);
1015
1016 local_offer->AddGroup(bundle_group);
1017 remote_answer->AddGroup(bundle_group);
1018 EXPECT_TRUE(transport_controller_
1019 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1020 .ok());
1021 EXPECT_TRUE(transport_controller_
1022 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1023 .ok());
1024
1025 // Verifiy that only |kAudio1| and |kVideo1| are bundled.
1026 auto transport1 = transport_controller_->GetRtpTransport(kAudioMid1);
1027 auto transport2 = transport_controller_->GetRtpTransport(kAudioMid2);
1028 auto transport3 = transport_controller_->GetRtpTransport(kVideoMid1);
1029 EXPECT_NE(transport1, transport2);
1030 EXPECT_EQ(transport1, transport3);
1031
1032 auto it = changed_rtp_transport_by_mid_.find(kVideoMid1);
1033 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1034 EXPECT_EQ(transport1, it->second);
1035 it = changed_rtp_transport_by_mid_.find(kAudioMid2);
1036 EXPECT_TRUE(it == changed_rtp_transport_by_mid_.end());
1037}
1038
1039// Tests that the initial offer/answer only have data section and audio/video
1040// sections are added in the subsequent offer.
1041TEST_F(JsepTransportControllerTest, BundleOnDataSectionInSubsequentOffer) {
1042 CreateJsepTransportController(JsepTransportController::Config());
1043 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1044 bundle_group.AddContentName(kDataMid1);
1045
1046 auto local_offer = rtc::MakeUnique<cricket::SessionDescription>();
1047 AddDataSection(local_offer.get(), kDataMid1,
1048 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
1049 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1050 nullptr);
1051 auto remote_answer = rtc::MakeUnique<cricket::SessionDescription>();
1052 AddDataSection(remote_answer.get(), kDataMid1,
1053 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
1054 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1055 nullptr);
1056 local_offer->AddGroup(bundle_group);
1057 remote_answer->AddGroup(bundle_group);
1058
1059 EXPECT_TRUE(transport_controller_
1060 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1061 .ok());
1062 EXPECT_TRUE(transport_controller_
1063 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1064 .ok());
1065 auto data_transport = transport_controller_->GetRtpTransport(kDataMid1);
1066
1067 // Add audio/video sections in subsequent offer.
1068 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1069 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1070 nullptr);
1071 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1072 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1073 nullptr);
1074 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1075 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1076 nullptr);
1077 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1078 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1079 nullptr);
1080
1081 // Reset the bundle group and do another offer/answer exchange.
1082 bundle_group.AddContentName(kAudioMid1);
1083 bundle_group.AddContentName(kVideoMid1);
1084 local_offer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1085 remote_answer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1086 local_offer->AddGroup(bundle_group);
1087 remote_answer->AddGroup(bundle_group);
1088
1089 EXPECT_TRUE(transport_controller_
1090 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1091 .ok());
1092 EXPECT_TRUE(transport_controller_
1093 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1094 .ok());
1095
1096 auto audio_transport = transport_controller_->GetRtpTransport(kAudioMid1);
1097 auto video_transport = transport_controller_->GetRtpTransport(kVideoMid1);
1098 EXPECT_EQ(data_transport, audio_transport);
1099 EXPECT_EQ(data_transport, video_transport);
1100}
1101
1102TEST_F(JsepTransportControllerTest, VideoDataRejectedInAnswer) {
1103 CreateJsepTransportController(JsepTransportController::Config());
1104 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1105 bundle_group.AddContentName(kAudioMid1);
1106 bundle_group.AddContentName(kVideoMid1);
1107 bundle_group.AddContentName(kDataMid1);
1108
1109 auto local_offer = rtc::MakeUnique<cricket::SessionDescription>();
1110 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1111 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1112 nullptr);
1113 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1114 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1115 nullptr);
1116 AddDataSection(local_offer.get(), kDataMid1,
1117 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1118 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1119 nullptr);
1120
1121 auto remote_answer = rtc::MakeUnique<cricket::SessionDescription>();
1122 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1123 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1124 nullptr);
1125 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1126 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1127 nullptr);
1128 AddDataSection(remote_answer.get(), kDataMid1,
1129 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1130 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1131 nullptr);
1132 // Reject video and data section.
1133 remote_answer->contents()[1].rejected = true;
1134 remote_answer->contents()[2].rejected = true;
1135
1136 local_offer->AddGroup(bundle_group);
1137 remote_answer->AddGroup(bundle_group);
1138
1139 EXPECT_TRUE(transport_controller_
1140 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1141 .ok());
1142 EXPECT_TRUE(transport_controller_
1143 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1144 .ok());
1145
1146 // Verify the RtpTransport/DtlsTransport is destroyed correctly.
1147 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kVideoMid1));
1148 EXPECT_EQ(nullptr, transport_controller_->GetDtlsTransport(kDataMid1));
1149 // Verify the signals are fired correctly.
1150 auto it = changed_rtp_transport_by_mid_.find(kVideoMid1);
1151 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1152 EXPECT_EQ(nullptr, it->second);
1153 auto it2 = changed_dtls_transport_by_mid_.find(kDataMid1);
1154 ASSERT_TRUE(it2 != changed_dtls_transport_by_mid_.end());
1155 EXPECT_EQ(nullptr, it2->second);
1156}
1157
1158// Tests that changing the bundled MID in subsequent offer/answer exchange is
1159// not supported.
1160// TODO(bugs.webrtc.org/6704): Change this test to expect success once issue is
1161// fixed
1162TEST_F(JsepTransportControllerTest, ChangeBundledMidNotSupported) {
1163 CreateJsepTransportController(JsepTransportController::Config());
1164 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1165 bundle_group.AddContentName(kAudioMid1);
1166 bundle_group.AddContentName(kVideoMid1);
1167
1168 auto local_offer = rtc::MakeUnique<cricket::SessionDescription>();
1169 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1170 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1171 nullptr);
1172 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1173 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1174 nullptr);
1175
1176 auto remote_answer = rtc::MakeUnique<cricket::SessionDescription>();
1177 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1178 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1179 nullptr);
1180 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1181 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1182 nullptr);
1183
1184 local_offer->AddGroup(bundle_group);
1185 remote_answer->AddGroup(bundle_group);
1186 EXPECT_TRUE(transport_controller_
1187 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1188 .ok());
1189 EXPECT_TRUE(transport_controller_
1190 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1191 .ok());
1192 EXPECT_EQ(transport_controller_->GetRtpTransport(kAudioMid1),
1193 transport_controller_->GetRtpTransport(kVideoMid1));
1194
1195 // Reorder the bundle group.
1196 EXPECT_TRUE(bundle_group.RemoveContentName(kAudioMid1));
1197 bundle_group.AddContentName(kAudioMid1);
1198 // The answerer uses the new bundle group and now the bundle mid is changed to
1199 // |kVideo1|.
1200 remote_answer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1201 remote_answer->AddGroup(bundle_group);
1202 EXPECT_TRUE(transport_controller_
1203 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1204 .ok());
1205 EXPECT_FALSE(transport_controller_
1206 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1207 .ok());
1208}
1209
1210} // namespace webrtc