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