1 /*
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 "pc/jsep_transport_controller.h"
12
13 #include <map>
14 #include <string>
15 #include <utility>
16
17 #include "api/dtls_transport_interface.h"
18 #include "api/transport/enums.h"
19 #include "p2p/base/candidate_pair_interface.h"
20 #include "p2p/base/dtls_transport_factory.h"
21 #include "p2p/base/fake_dtls_transport.h"
22 #include "p2p/base/fake_ice_transport.h"
23 #include "p2p/base/p2p_constants.h"
24 #include "p2p/base/transport_info.h"
25 #include "rtc_base/fake_ssl_identity.h"
26 #include "rtc_base/gunit.h"
27 #include "rtc_base/logging.h"
28 #include "rtc_base/net_helper.h"
29 #include "rtc_base/socket_address.h"
30 #include "rtc_base/ssl_fingerprint.h"
31 #include "rtc_base/ssl_identity.h"
32 #include "rtc_base/task_queue_for_test.h"
33 #include "rtc_base/thread.h"
34 #include "test/gtest.h"
35 #include "test/scoped_key_value_config.h"
36
37 using cricket::Candidate;
38 using cricket::Candidates;
39 using cricket::FakeDtlsTransport;
40 using webrtc::SdpType;
41
42 static const int kTimeout = 100;
43 static const char kIceUfrag1[] = "u0001";
44 static const char kIcePwd1[] = "TESTICEPWD00000000000001";
45 static const char kIceUfrag2[] = "u0002";
46 static const char kIcePwd2[] = "TESTICEPWD00000000000002";
47 static const char kIceUfrag3[] = "u0003";
48 static const char kIcePwd3[] = "TESTICEPWD00000000000003";
49 static const char kIceUfrag4[] = "u0004";
50 static const char kIcePwd4[] = "TESTICEPWD00000000000004";
51 static const char kAudioMid1[] = "audio1";
52 static const char kAudioMid2[] = "audio2";
53 static const char kVideoMid1[] = "video1";
54 static const char kVideoMid2[] = "video2";
55 static const char kDataMid1[] = "data1";
56
57 namespace webrtc {
58
59 class FakeIceTransportFactory : public webrtc::IceTransportFactory {
60 public:
61 ~FakeIceTransportFactory() override = default;
CreateIceTransport(const std::string & transport_name,int component,IceTransportInit init)62 rtc::scoped_refptr<IceTransportInterface> CreateIceTransport(
63 const std::string& transport_name,
64 int component,
65 IceTransportInit init) override {
66 return rtc::make_ref_counted<cricket::FakeIceTransportWrapper>(
67 std::make_unique<cricket::FakeIceTransport>(transport_name, component));
68 }
69 };
70
71 class FakeDtlsTransportFactory : public cricket::DtlsTransportFactory {
72 public:
CreateDtlsTransport(cricket::IceTransportInternal * ice,const webrtc::CryptoOptions & crypto_options,rtc::SSLProtocolVersion max_version)73 std::unique_ptr<cricket::DtlsTransportInternal> CreateDtlsTransport(
74 cricket::IceTransportInternal* ice,
75 const webrtc::CryptoOptions& crypto_options,
76 rtc::SSLProtocolVersion max_version) override {
77 return std::make_unique<FakeDtlsTransport>(
78 static_cast<cricket::FakeIceTransport*>(ice));
79 }
80 };
81
82 class JsepTransportControllerTest : public JsepTransportController::Observer,
83 public ::testing::Test,
84 public sigslot::has_slots<> {
85 public:
JsepTransportControllerTest()86 JsepTransportControllerTest() : signaling_thread_(rtc::Thread::Current()) {
87 fake_ice_transport_factory_ = std::make_unique<FakeIceTransportFactory>();
88 fake_dtls_transport_factory_ = std::make_unique<FakeDtlsTransportFactory>();
89 }
90
CreateJsepTransportController(JsepTransportController::Config config,rtc::Thread * network_thread=rtc::Thread::Current (),cricket::PortAllocator * port_allocator=nullptr)91 void CreateJsepTransportController(
92 JsepTransportController::Config config,
93 rtc::Thread* network_thread = rtc::Thread::Current(),
94 cricket::PortAllocator* port_allocator = nullptr) {
95 config.transport_observer = this;
96 config.rtcp_handler = [](const rtc::CopyOnWriteBuffer& packet,
97 int64_t packet_time_us) {
98 RTC_DCHECK_NOTREACHED();
99 };
100 config.ice_transport_factory = fake_ice_transport_factory_.get();
101 config.dtls_transport_factory = fake_dtls_transport_factory_.get();
102 config.on_dtls_handshake_error_ = [](rtc::SSLHandshakeError s) {};
103 config.field_trials = &field_trials_;
104 transport_controller_ = std::make_unique<JsepTransportController>(
105 network_thread, port_allocator, nullptr /* async_resolver_factory */,
106 config);
107 SendTask(network_thread, [&] { ConnectTransportControllerSignals(); });
108 }
109
ConnectTransportControllerSignals()110 void ConnectTransportControllerSignals() {
111 transport_controller_->SubscribeIceConnectionState(
112 [this](cricket::IceConnectionState s) {
113 JsepTransportControllerTest::OnConnectionState(s);
114 });
115 transport_controller_->SubscribeConnectionState(
116 [this](PeerConnectionInterface::PeerConnectionState s) {
117 JsepTransportControllerTest::OnCombinedConnectionState(s);
118 });
119 transport_controller_->SubscribeStandardizedIceConnectionState(
120 [this](PeerConnectionInterface::IceConnectionState s) {
121 JsepTransportControllerTest::OnStandardizedIceConnectionState(s);
122 });
123 transport_controller_->SubscribeIceGatheringState(
124 [this](cricket::IceGatheringState s) {
125 JsepTransportControllerTest::OnGatheringState(s);
126 });
127 transport_controller_->SubscribeIceCandidateGathered(
128 [this](const std::string& transport,
129 const std::vector<cricket::Candidate>& candidates) {
130 JsepTransportControllerTest::OnCandidatesGathered(transport,
131 candidates);
132 });
133 }
134
135 std::unique_ptr<cricket::SessionDescription>
CreateSessionDescriptionWithoutBundle()136 CreateSessionDescriptionWithoutBundle() {
137 auto description = std::make_unique<cricket::SessionDescription>();
138 AddAudioSection(description.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
139 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
140 nullptr);
141 AddVideoSection(description.get(), kVideoMid1, kIceUfrag1, kIcePwd1,
142 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
143 nullptr);
144 return description;
145 }
146
147 std::unique_ptr<cricket::SessionDescription>
CreateSessionDescriptionWithBundleGroup()148 CreateSessionDescriptionWithBundleGroup() {
149 auto description = CreateSessionDescriptionWithoutBundle();
150 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
151 bundle_group.AddContentName(kAudioMid1);
152 bundle_group.AddContentName(kVideoMid1);
153 description->AddGroup(bundle_group);
154
155 return description;
156 }
157
158 std::unique_ptr<cricket::SessionDescription>
CreateSessionDescriptionWithBundledData()159 CreateSessionDescriptionWithBundledData() {
160 auto description = CreateSessionDescriptionWithoutBundle();
161 AddDataSection(description.get(), kDataMid1,
162 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
163 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
164 nullptr);
165 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
166 bundle_group.AddContentName(kAudioMid1);
167 bundle_group.AddContentName(kVideoMid1);
168 bundle_group.AddContentName(kDataMid1);
169 description->AddGroup(bundle_group);
170 return description;
171 }
172
AddAudioSection(cricket::SessionDescription * description,const std::string & mid,const std::string & ufrag,const std::string & pwd,cricket::IceMode ice_mode,cricket::ConnectionRole conn_role,rtc::scoped_refptr<rtc::RTCCertificate> cert)173 void AddAudioSection(cricket::SessionDescription* description,
174 const std::string& mid,
175 const std::string& ufrag,
176 const std::string& pwd,
177 cricket::IceMode ice_mode,
178 cricket::ConnectionRole conn_role,
179 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
180 std::unique_ptr<cricket::AudioContentDescription> audio(
181 new cricket::AudioContentDescription());
182 // Set RTCP-mux to be true because the default policy is "mux required".
183 audio->set_rtcp_mux(true);
184 description->AddContent(mid, cricket::MediaProtocolType::kRtp,
185 /*rejected=*/false, std::move(audio));
186 AddTransportInfo(description, mid, ufrag, pwd, ice_mode, conn_role, cert);
187 }
188
AddVideoSection(cricket::SessionDescription * description,const std::string & mid,const std::string & ufrag,const std::string & pwd,cricket::IceMode ice_mode,cricket::ConnectionRole conn_role,rtc::scoped_refptr<rtc::RTCCertificate> cert)189 void AddVideoSection(cricket::SessionDescription* description,
190 const std::string& mid,
191 const std::string& ufrag,
192 const std::string& pwd,
193 cricket::IceMode ice_mode,
194 cricket::ConnectionRole conn_role,
195 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
196 std::unique_ptr<cricket::VideoContentDescription> video(
197 new cricket::VideoContentDescription());
198 // Set RTCP-mux to be true because the default policy is "mux required".
199 video->set_rtcp_mux(true);
200 description->AddContent(mid, cricket::MediaProtocolType::kRtp,
201 /*rejected=*/false, std::move(video));
202 AddTransportInfo(description, mid, ufrag, pwd, ice_mode, conn_role, cert);
203 }
204
AddDataSection(cricket::SessionDescription * description,const std::string & mid,cricket::MediaProtocolType protocol_type,const std::string & ufrag,const std::string & pwd,cricket::IceMode ice_mode,cricket::ConnectionRole conn_role,rtc::scoped_refptr<rtc::RTCCertificate> cert)205 void AddDataSection(cricket::SessionDescription* description,
206 const std::string& mid,
207 cricket::MediaProtocolType protocol_type,
208 const std::string& ufrag,
209 const std::string& pwd,
210 cricket::IceMode ice_mode,
211 cricket::ConnectionRole conn_role,
212 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
213 RTC_CHECK(protocol_type == cricket::MediaProtocolType::kSctp);
214 std::unique_ptr<cricket::SctpDataContentDescription> data(
215 new cricket::SctpDataContentDescription());
216 data->set_rtcp_mux(true);
217 description->AddContent(mid, protocol_type,
218 /*rejected=*/false, std::move(data));
219 AddTransportInfo(description, mid, ufrag, pwd, ice_mode, conn_role, cert);
220 }
221
AddTransportInfo(cricket::SessionDescription * description,const std::string & mid,const std::string & ufrag,const std::string & pwd,cricket::IceMode ice_mode,cricket::ConnectionRole conn_role,rtc::scoped_refptr<rtc::RTCCertificate> cert)222 void AddTransportInfo(cricket::SessionDescription* description,
223 const std::string& mid,
224 const std::string& ufrag,
225 const std::string& pwd,
226 cricket::IceMode ice_mode,
227 cricket::ConnectionRole conn_role,
228 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
229 std::unique_ptr<rtc::SSLFingerprint> fingerprint;
230 if (cert) {
231 fingerprint = rtc::SSLFingerprint::CreateFromCertificate(*cert);
232 }
233
234 cricket::TransportDescription transport_desc(std::vector<std::string>(),
235 ufrag, pwd, ice_mode,
236 conn_role, fingerprint.get());
237 description->AddTransportInfo(cricket::TransportInfo(mid, transport_desc));
238 }
239
CreateIceConfig(int receiving_timeout,cricket::ContinualGatheringPolicy continual_gathering_policy)240 cricket::IceConfig CreateIceConfig(
241 int receiving_timeout,
242 cricket::ContinualGatheringPolicy continual_gathering_policy) {
243 cricket::IceConfig config;
244 config.receiving_timeout = receiving_timeout;
245 config.continual_gathering_policy = continual_gathering_policy;
246 return config;
247 }
248
CreateCandidate(const std::string & transport_name,int component)249 Candidate CreateCandidate(const std::string& transport_name, int component) {
250 Candidate c;
251 c.set_transport_name(transport_name);
252 c.set_address(rtc::SocketAddress("192.168.1.1", 8000));
253 c.set_component(component);
254 c.set_protocol(cricket::UDP_PROTOCOL_NAME);
255 c.set_priority(1);
256 return c;
257 }
258
CreateLocalDescriptionAndCompleteConnectionOnNetworkThread()259 void CreateLocalDescriptionAndCompleteConnectionOnNetworkThread() {
260 if (!network_thread_->IsCurrent()) {
261 SendTask(network_thread_.get(), [&] {
262 CreateLocalDescriptionAndCompleteConnectionOnNetworkThread();
263 });
264 return;
265 }
266
267 auto description = CreateSessionDescriptionWithBundleGroup();
268 EXPECT_TRUE(transport_controller_
269 ->SetLocalDescription(SdpType::kOffer, description.get())
270 .ok());
271
272 transport_controller_->MaybeStartGathering();
273 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
274 transport_controller_->GetDtlsTransport(kAudioMid1));
275 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
276 transport_controller_->GetDtlsTransport(kVideoMid1));
277 fake_audio_dtls->fake_ice_transport()->SignalCandidateGathered(
278 fake_audio_dtls->fake_ice_transport(),
279 CreateCandidate(kAudioMid1, /*component=*/1));
280 fake_video_dtls->fake_ice_transport()->SignalCandidateGathered(
281 fake_video_dtls->fake_ice_transport(),
282 CreateCandidate(kVideoMid1, /*component=*/1));
283 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
284 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
285 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(2);
286 fake_video_dtls->fake_ice_transport()->SetConnectionCount(2);
287 fake_audio_dtls->SetReceiving(true);
288 fake_video_dtls->SetReceiving(true);
289 fake_audio_dtls->SetWritable(true);
290 fake_video_dtls->SetWritable(true);
291 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
292 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
293 }
294
295 protected:
OnConnectionState(cricket::IceConnectionState state)296 void OnConnectionState(cricket::IceConnectionState state) {
297 ice_signaled_on_thread_ = rtc::Thread::Current();
298 connection_state_ = state;
299 ++connection_state_signal_count_;
300 }
301
OnStandardizedIceConnectionState(PeerConnectionInterface::IceConnectionState state)302 void OnStandardizedIceConnectionState(
303 PeerConnectionInterface::IceConnectionState state) {
304 ice_signaled_on_thread_ = rtc::Thread::Current();
305 ice_connection_state_ = state;
306 ++ice_connection_state_signal_count_;
307 }
308
OnCombinedConnectionState(PeerConnectionInterface::PeerConnectionState state)309 void OnCombinedConnectionState(
310 PeerConnectionInterface::PeerConnectionState state) {
311 RTC_LOG(LS_INFO) << "OnCombinedConnectionState: "
312 << static_cast<int>(state);
313 ice_signaled_on_thread_ = rtc::Thread::Current();
314 combined_connection_state_ = state;
315 ++combined_connection_state_signal_count_;
316 }
317
OnGatheringState(cricket::IceGatheringState state)318 void OnGatheringState(cricket::IceGatheringState state) {
319 ice_signaled_on_thread_ = rtc::Thread::Current();
320 gathering_state_ = state;
321 ++gathering_state_signal_count_;
322 }
323
OnCandidatesGathered(const std::string & transport_name,const Candidates & candidates)324 void OnCandidatesGathered(const std::string& transport_name,
325 const Candidates& candidates) {
326 ice_signaled_on_thread_ = rtc::Thread::Current();
327 candidates_[transport_name].insert(candidates_[transport_name].end(),
328 candidates.begin(), candidates.end());
329 ++candidates_signal_count_;
330 }
331
332 // JsepTransportController::Observer overrides.
OnTransportChanged(const std::string & mid,RtpTransportInternal * rtp_transport,rtc::scoped_refptr<DtlsTransport> dtls_transport,DataChannelTransportInterface * data_channel_transport)333 bool OnTransportChanged(
334 const std::string& mid,
335 RtpTransportInternal* rtp_transport,
336 rtc::scoped_refptr<DtlsTransport> dtls_transport,
337 DataChannelTransportInterface* data_channel_transport) override {
338 changed_rtp_transport_by_mid_[mid] = rtp_transport;
339 if (dtls_transport) {
340 changed_dtls_transport_by_mid_[mid] = dtls_transport->internal();
341 } else {
342 changed_dtls_transport_by_mid_[mid] = nullptr;
343 }
344 return true;
345 }
346
347 rtc::AutoThread main_thread_;
348 // Information received from signals from transport controller.
349 cricket::IceConnectionState connection_state_ =
350 cricket::kIceConnectionConnecting;
351 PeerConnectionInterface::IceConnectionState ice_connection_state_ =
352 PeerConnectionInterface::kIceConnectionNew;
353 PeerConnectionInterface::PeerConnectionState combined_connection_state_ =
354 PeerConnectionInterface::PeerConnectionState::kNew;
355 bool receiving_ = false;
356 cricket::IceGatheringState gathering_state_ = cricket::kIceGatheringNew;
357 // transport_name => candidates
358 std::map<std::string, Candidates> candidates_;
359 // Counts of each signal emitted.
360 int connection_state_signal_count_ = 0;
361 int ice_connection_state_signal_count_ = 0;
362 int combined_connection_state_signal_count_ = 0;
363 int receiving_signal_count_ = 0;
364 int gathering_state_signal_count_ = 0;
365 int candidates_signal_count_ = 0;
366
367 // `network_thread_` should be destroyed after `transport_controller_`
368 std::unique_ptr<rtc::Thread> network_thread_;
369 std::unique_ptr<FakeIceTransportFactory> fake_ice_transport_factory_;
370 std::unique_ptr<FakeDtlsTransportFactory> fake_dtls_transport_factory_;
371 rtc::Thread* const signaling_thread_ = nullptr;
372 rtc::Thread* ice_signaled_on_thread_ = nullptr;
373 // Used to verify the SignalRtpTransportChanged/SignalDtlsTransportChanged are
374 // signaled correctly.
375 std::map<std::string, RtpTransportInternal*> changed_rtp_transport_by_mid_;
376 std::map<std::string, cricket::DtlsTransportInternal*>
377 changed_dtls_transport_by_mid_;
378
379 // Transport controller needs to be destroyed first, because it may issue
380 // callbacks that modify the changed_*_by_mid in the destructor.
381 std::unique_ptr<JsepTransportController> transport_controller_;
382 webrtc::test::ScopedKeyValueConfig field_trials_;
383 };
384
TEST_F(JsepTransportControllerTest,GetRtpTransport)385 TEST_F(JsepTransportControllerTest, GetRtpTransport) {
386 CreateJsepTransportController(JsepTransportController::Config());
387 auto description = CreateSessionDescriptionWithoutBundle();
388 EXPECT_TRUE(transport_controller_
389 ->SetLocalDescription(SdpType::kOffer, description.get())
390 .ok());
391 auto audio_rtp_transport = transport_controller_->GetRtpTransport(kAudioMid1);
392 auto video_rtp_transport = transport_controller_->GetRtpTransport(kVideoMid1);
393 EXPECT_NE(nullptr, audio_rtp_transport);
394 EXPECT_NE(nullptr, video_rtp_transport);
395 EXPECT_NE(audio_rtp_transport, video_rtp_transport);
396 // Return nullptr for non-existing ones.
397 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kAudioMid2));
398 }
399
TEST_F(JsepTransportControllerTest,GetDtlsTransport)400 TEST_F(JsepTransportControllerTest, GetDtlsTransport) {
401 JsepTransportController::Config config;
402 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
403 CreateJsepTransportController(config);
404 auto description = CreateSessionDescriptionWithoutBundle();
405 EXPECT_TRUE(transport_controller_
406 ->SetLocalDescription(SdpType::kOffer, description.get())
407 .ok());
408 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kAudioMid1));
409 EXPECT_NE(nullptr, transport_controller_->GetRtcpDtlsTransport(kAudioMid1));
410 EXPECT_NE(nullptr,
411 transport_controller_->LookupDtlsTransportByMid(kAudioMid1));
412 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kVideoMid1));
413 EXPECT_NE(nullptr, transport_controller_->GetRtcpDtlsTransport(kVideoMid1));
414 EXPECT_NE(nullptr,
415 transport_controller_->LookupDtlsTransportByMid(kVideoMid1));
416 // Lookup for all MIDs should return different transports (no bundle)
417 EXPECT_NE(transport_controller_->LookupDtlsTransportByMid(kAudioMid1),
418 transport_controller_->LookupDtlsTransportByMid(kVideoMid1));
419 // Return nullptr for non-existing ones.
420 EXPECT_EQ(nullptr, transport_controller_->GetDtlsTransport(kVideoMid2));
421 EXPECT_EQ(nullptr, transport_controller_->GetRtcpDtlsTransport(kVideoMid2));
422 EXPECT_EQ(nullptr,
423 transport_controller_->LookupDtlsTransportByMid(kVideoMid2));
424 // Take a pointer to a transport, shut down the transport controller,
425 // and verify that the resulting container is empty.
426 auto dtls_transport =
427 transport_controller_->LookupDtlsTransportByMid(kVideoMid1);
428 webrtc::DtlsTransport* my_transport =
429 static_cast<DtlsTransport*>(dtls_transport.get());
430 EXPECT_NE(nullptr, my_transport->internal());
431 transport_controller_.reset();
432 EXPECT_EQ(nullptr, my_transport->internal());
433 }
434
TEST_F(JsepTransportControllerTest,GetDtlsTransportWithRtcpMux)435 TEST_F(JsepTransportControllerTest, GetDtlsTransportWithRtcpMux) {
436 JsepTransportController::Config config;
437 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
438 CreateJsepTransportController(config);
439 auto description = CreateSessionDescriptionWithoutBundle();
440 EXPECT_TRUE(transport_controller_
441 ->SetLocalDescription(SdpType::kOffer, description.get())
442 .ok());
443 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kAudioMid1));
444 EXPECT_EQ(nullptr, transport_controller_->GetRtcpDtlsTransport(kAudioMid1));
445 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kVideoMid1));
446 EXPECT_EQ(nullptr, transport_controller_->GetRtcpDtlsTransport(kVideoMid1));
447 }
448
TEST_F(JsepTransportControllerTest,SetIceConfig)449 TEST_F(JsepTransportControllerTest, SetIceConfig) {
450 CreateJsepTransportController(JsepTransportController::Config());
451 auto description = CreateSessionDescriptionWithoutBundle();
452 EXPECT_TRUE(transport_controller_
453 ->SetLocalDescription(SdpType::kOffer, description.get())
454 .ok());
455
456 transport_controller_->SetIceConfig(
457 CreateIceConfig(kTimeout, cricket::GATHER_CONTINUALLY));
458 FakeDtlsTransport* fake_audio_dtls = static_cast<FakeDtlsTransport*>(
459 transport_controller_->GetDtlsTransport(kAudioMid1));
460 ASSERT_NE(nullptr, fake_audio_dtls);
461 EXPECT_EQ(kTimeout,
462 fake_audio_dtls->fake_ice_transport()->receiving_timeout());
463 EXPECT_TRUE(fake_audio_dtls->fake_ice_transport()->gather_continually());
464
465 // Test that value stored in controller is applied to new transports.
466 AddAudioSection(description.get(), kAudioMid2, kIceUfrag1, kIcePwd1,
467 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
468 nullptr);
469
470 EXPECT_TRUE(transport_controller_
471 ->SetLocalDescription(SdpType::kOffer, description.get())
472 .ok());
473 fake_audio_dtls = static_cast<FakeDtlsTransport*>(
474 transport_controller_->GetDtlsTransport(kAudioMid2));
475 ASSERT_NE(nullptr, fake_audio_dtls);
476 EXPECT_EQ(kTimeout,
477 fake_audio_dtls->fake_ice_transport()->receiving_timeout());
478 EXPECT_TRUE(fake_audio_dtls->fake_ice_transport()->gather_continually());
479 }
480
481 // Tests the getter and setter of the ICE restart flag.
TEST_F(JsepTransportControllerTest,NeedIceRestart)482 TEST_F(JsepTransportControllerTest, NeedIceRestart) {
483 CreateJsepTransportController(JsepTransportController::Config());
484 auto description = CreateSessionDescriptionWithoutBundle();
485 EXPECT_TRUE(transport_controller_
486 ->SetLocalDescription(SdpType::kOffer, description.get())
487 .ok());
488 EXPECT_TRUE(transport_controller_
489 ->SetRemoteDescription(SdpType::kAnswer, description.get())
490 .ok());
491
492 // Initially NeedsIceRestart should return false.
493 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kAudioMid1));
494 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kVideoMid1));
495 // Set the needs-ice-restart flag and verify NeedsIceRestart starts returning
496 // true.
497 transport_controller_->SetNeedsIceRestartFlag();
498 EXPECT_TRUE(transport_controller_->NeedsIceRestart(kAudioMid1));
499 EXPECT_TRUE(transport_controller_->NeedsIceRestart(kVideoMid1));
500 // For a nonexistent transport, false should be returned.
501 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kVideoMid2));
502
503 // Reset the ice_ufrag/ice_pwd for audio.
504 auto audio_transport_info = description->GetTransportInfoByName(kAudioMid1);
505 audio_transport_info->description.ice_ufrag = kIceUfrag2;
506 audio_transport_info->description.ice_pwd = kIcePwd2;
507 EXPECT_TRUE(transport_controller_
508 ->SetLocalDescription(SdpType::kOffer, description.get())
509 .ok());
510 // Because the ICE is only restarted for audio, NeedsIceRestart is expected to
511 // return false for audio and true for video.
512 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kAudioMid1));
513 EXPECT_TRUE(transport_controller_->NeedsIceRestart(kVideoMid1));
514 }
515
TEST_F(JsepTransportControllerTest,MaybeStartGathering)516 TEST_F(JsepTransportControllerTest, MaybeStartGathering) {
517 CreateJsepTransportController(JsepTransportController::Config());
518 auto description = CreateSessionDescriptionWithBundleGroup();
519 EXPECT_TRUE(transport_controller_
520 ->SetLocalDescription(SdpType::kOffer, description.get())
521 .ok());
522 // After setting the local description, we should be able to start gathering
523 // candidates.
524 transport_controller_->MaybeStartGathering();
525 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
526 EXPECT_EQ(1, gathering_state_signal_count_);
527 }
528
TEST_F(JsepTransportControllerTest,AddRemoveRemoteCandidates)529 TEST_F(JsepTransportControllerTest, AddRemoveRemoteCandidates) {
530 CreateJsepTransportController(JsepTransportController::Config());
531 auto description = CreateSessionDescriptionWithoutBundle();
532 transport_controller_->SetLocalDescription(SdpType::kOffer,
533 description.get());
534 transport_controller_->SetRemoteDescription(SdpType::kAnswer,
535 description.get());
536 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
537 transport_controller_->GetDtlsTransport(kAudioMid1));
538 ASSERT_NE(nullptr, fake_audio_dtls);
539 Candidates candidates;
540 candidates.push_back(
541 CreateCandidate(kAudioMid1, cricket::ICE_CANDIDATE_COMPONENT_RTP));
542 EXPECT_TRUE(
543 transport_controller_->AddRemoteCandidates(kAudioMid1, candidates).ok());
544 EXPECT_EQ(1U,
545 fake_audio_dtls->fake_ice_transport()->remote_candidates().size());
546
547 EXPECT_TRUE(transport_controller_->RemoveRemoteCandidates(candidates).ok());
548 EXPECT_EQ(0U,
549 fake_audio_dtls->fake_ice_transport()->remote_candidates().size());
550 }
551
TEST_F(JsepTransportControllerTest,SetAndGetLocalCertificate)552 TEST_F(JsepTransportControllerTest, SetAndGetLocalCertificate) {
553 CreateJsepTransportController(JsepTransportController::Config());
554
555 rtc::scoped_refptr<rtc::RTCCertificate> certificate1 =
556 rtc::RTCCertificate::Create(
557 rtc::SSLIdentity::Create("session1", rtc::KT_DEFAULT));
558 rtc::scoped_refptr<rtc::RTCCertificate> returned_certificate;
559
560 auto description = std::make_unique<cricket::SessionDescription>();
561 AddAudioSection(description.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
562 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
563 certificate1);
564
565 // Apply the local certificate.
566 EXPECT_TRUE(transport_controller_->SetLocalCertificate(certificate1));
567 // Apply the local description.
568 EXPECT_TRUE(transport_controller_
569 ->SetLocalDescription(SdpType::kOffer, description.get())
570 .ok());
571 returned_certificate = transport_controller_->GetLocalCertificate(kAudioMid1);
572 EXPECT_TRUE(returned_certificate);
573 EXPECT_EQ(certificate1->identity()->certificate().ToPEMString(),
574 returned_certificate->identity()->certificate().ToPEMString());
575
576 // Should fail if called for a nonexistant transport.
577 EXPECT_EQ(nullptr, transport_controller_->GetLocalCertificate(kVideoMid1));
578
579 // Shouldn't be able to change the identity once set.
580 rtc::scoped_refptr<rtc::RTCCertificate> certificate2 =
581 rtc::RTCCertificate::Create(
582 rtc::SSLIdentity::Create("session2", rtc::KT_DEFAULT));
583 EXPECT_FALSE(transport_controller_->SetLocalCertificate(certificate2));
584 }
585
TEST_F(JsepTransportControllerTest,GetRemoteSSLCertChain)586 TEST_F(JsepTransportControllerTest, GetRemoteSSLCertChain) {
587 CreateJsepTransportController(JsepTransportController::Config());
588 auto description = CreateSessionDescriptionWithBundleGroup();
589 EXPECT_TRUE(transport_controller_
590 ->SetLocalDescription(SdpType::kOffer, description.get())
591 .ok());
592 rtc::FakeSSLCertificate fake_certificate("fake_data");
593
594 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
595 transport_controller_->GetDtlsTransport(kAudioMid1));
596 fake_audio_dtls->SetRemoteSSLCertificate(&fake_certificate);
597 std::unique_ptr<rtc::SSLCertChain> returned_cert_chain =
598 transport_controller_->GetRemoteSSLCertChain(kAudioMid1);
599 ASSERT_TRUE(returned_cert_chain);
600 ASSERT_EQ(1u, returned_cert_chain->GetSize());
601 EXPECT_EQ(fake_certificate.ToPEMString(),
602 returned_cert_chain->Get(0).ToPEMString());
603
604 // Should fail if called for a nonexistant transport.
605 EXPECT_FALSE(transport_controller_->GetRemoteSSLCertChain(kAudioMid2));
606 }
607
TEST_F(JsepTransportControllerTest,GetDtlsRole)608 TEST_F(JsepTransportControllerTest, GetDtlsRole) {
609 CreateJsepTransportController(JsepTransportController::Config());
610 auto offer_certificate = rtc::RTCCertificate::Create(
611 rtc::SSLIdentity::Create("offer", rtc::KT_DEFAULT));
612 auto answer_certificate = rtc::RTCCertificate::Create(
613 rtc::SSLIdentity::Create("answer", rtc::KT_DEFAULT));
614 transport_controller_->SetLocalCertificate(offer_certificate);
615
616 auto offer_desc = std::make_unique<cricket::SessionDescription>();
617 AddAudioSection(offer_desc.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
618 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
619 offer_certificate);
620 auto answer_desc = std::make_unique<cricket::SessionDescription>();
621 AddAudioSection(answer_desc.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
622 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
623 answer_certificate);
624
625 EXPECT_TRUE(transport_controller_
626 ->SetLocalDescription(SdpType::kOffer, offer_desc.get())
627 .ok());
628
629 absl::optional<rtc::SSLRole> role =
630 transport_controller_->GetDtlsRole(kAudioMid1);
631 // The DTLS role is not decided yet.
632 EXPECT_FALSE(role);
633 EXPECT_TRUE(transport_controller_
634 ->SetRemoteDescription(SdpType::kAnswer, answer_desc.get())
635 .ok());
636 role = transport_controller_->GetDtlsRole(kAudioMid1);
637
638 ASSERT_TRUE(role);
639 EXPECT_EQ(rtc::SSL_CLIENT, *role);
640 }
641
TEST_F(JsepTransportControllerTest,GetStats)642 TEST_F(JsepTransportControllerTest, GetStats) {
643 CreateJsepTransportController(JsepTransportController::Config());
644 auto description = CreateSessionDescriptionWithBundleGroup();
645 EXPECT_TRUE(transport_controller_
646 ->SetLocalDescription(SdpType::kOffer, description.get())
647 .ok());
648
649 cricket::TransportStats stats;
650 EXPECT_TRUE(transport_controller_->GetStats(kAudioMid1, &stats));
651 EXPECT_EQ(kAudioMid1, stats.transport_name);
652 EXPECT_EQ(1u, stats.channel_stats.size());
653 // Return false for non-existing transport.
654 EXPECT_FALSE(transport_controller_->GetStats(kAudioMid2, &stats));
655 }
656
TEST_F(JsepTransportControllerTest,SignalConnectionStateFailed)657 TEST_F(JsepTransportControllerTest, SignalConnectionStateFailed) {
658 CreateJsepTransportController(JsepTransportController::Config());
659 auto description = CreateSessionDescriptionWithoutBundle();
660 EXPECT_TRUE(transport_controller_
661 ->SetLocalDescription(SdpType::kOffer, description.get())
662 .ok());
663
664 auto fake_ice = static_cast<cricket::FakeIceTransport*>(
665 transport_controller_->GetDtlsTransport(kAudioMid1)->ice_transport());
666 fake_ice->SetCandidatesGatheringComplete();
667 fake_ice->SetConnectionCount(1);
668 // The connection stats will be failed if there is no active connection.
669 fake_ice->SetConnectionCount(0);
670 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
671 EXPECT_EQ(1, connection_state_signal_count_);
672 EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionFailed,
673 ice_connection_state_, kTimeout);
674 EXPECT_EQ(1, ice_connection_state_signal_count_);
675 EXPECT_EQ_WAIT(PeerConnectionInterface::PeerConnectionState::kFailed,
676 combined_connection_state_, kTimeout);
677 EXPECT_EQ(1, combined_connection_state_signal_count_);
678 }
679
TEST_F(JsepTransportControllerTest,SignalConnectionStateConnectedNoMediaTransport)680 TEST_F(JsepTransportControllerTest,
681 SignalConnectionStateConnectedNoMediaTransport) {
682 CreateJsepTransportController(JsepTransportController::Config());
683 auto description = CreateSessionDescriptionWithoutBundle();
684 EXPECT_TRUE(transport_controller_
685 ->SetLocalDescription(SdpType::kOffer, description.get())
686 .ok());
687
688 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
689 transport_controller_->GetDtlsTransport(kAudioMid1));
690 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
691 transport_controller_->GetDtlsTransport(kVideoMid1));
692
693 // First, have one transport connect, and another fail, to ensure that
694 // the first transport connecting didn't trigger a "connected" state signal.
695 // We should only get a signal when all are connected.
696 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
697 fake_audio_dtls->SetWritable(true);
698 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
699 // Decrease the number of the connection to trigger the signal.
700 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
701 fake_video_dtls->fake_ice_transport()->SetConnectionCount(0);
702 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
703
704 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
705 EXPECT_EQ(1, connection_state_signal_count_);
706 EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionFailed,
707 ice_connection_state_, kTimeout);
708 EXPECT_EQ(2, ice_connection_state_signal_count_);
709 EXPECT_EQ_WAIT(PeerConnectionInterface::PeerConnectionState::kFailed,
710 combined_connection_state_, kTimeout);
711 EXPECT_EQ(2, combined_connection_state_signal_count_);
712
713 fake_audio_dtls->SetDtlsState(DtlsTransportState::kConnected);
714 fake_video_dtls->SetDtlsState(DtlsTransportState::kConnected);
715 // Set the connection count to be 2 and the cricket::FakeIceTransport will set
716 // the transport state to be STATE_CONNECTING.
717 fake_video_dtls->fake_ice_transport()->SetConnectionCount(2);
718 fake_video_dtls->SetWritable(true);
719 EXPECT_EQ_WAIT(cricket::kIceConnectionConnected, connection_state_, kTimeout);
720 EXPECT_EQ(2, connection_state_signal_count_);
721 EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionConnected,
722 ice_connection_state_, kTimeout);
723 EXPECT_EQ(3, ice_connection_state_signal_count_);
724 EXPECT_EQ_WAIT(PeerConnectionInterface::PeerConnectionState::kConnected,
725 combined_connection_state_, kTimeout);
726 EXPECT_EQ(3, combined_connection_state_signal_count_);
727 }
728
TEST_F(JsepTransportControllerTest,SignalConnectionStateComplete)729 TEST_F(JsepTransportControllerTest, SignalConnectionStateComplete) {
730 CreateJsepTransportController(JsepTransportController::Config());
731 auto description = CreateSessionDescriptionWithoutBundle();
732 EXPECT_TRUE(transport_controller_
733 ->SetLocalDescription(SdpType::kOffer, description.get())
734 .ok());
735
736 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
737 transport_controller_->GetDtlsTransport(kAudioMid1));
738 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
739 transport_controller_->GetDtlsTransport(kVideoMid1));
740
741 // First, have one transport connect, and another fail, to ensure that
742 // the first transport connecting didn't trigger a "connected" state signal.
743 // We should only get a signal when all are connected.
744 fake_audio_dtls->fake_ice_transport()->SetTransportState(
745 IceTransportState::kCompleted,
746 cricket::IceTransportState::STATE_COMPLETED);
747 fake_audio_dtls->SetWritable(true);
748 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
749
750 EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionChecking,
751 ice_connection_state_, kTimeout);
752 EXPECT_EQ(1, ice_connection_state_signal_count_);
753 EXPECT_EQ_WAIT(PeerConnectionInterface::PeerConnectionState::kConnecting,
754 combined_connection_state_, kTimeout);
755 EXPECT_EQ(1, combined_connection_state_signal_count_);
756
757 fake_video_dtls->fake_ice_transport()->SetTransportState(
758 IceTransportState::kFailed, cricket::IceTransportState::STATE_FAILED);
759 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
760
761 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
762 EXPECT_EQ(1, connection_state_signal_count_);
763 EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionFailed,
764 ice_connection_state_, kTimeout);
765 EXPECT_EQ(2, ice_connection_state_signal_count_);
766 EXPECT_EQ_WAIT(PeerConnectionInterface::PeerConnectionState::kFailed,
767 combined_connection_state_, kTimeout);
768 EXPECT_EQ(2, combined_connection_state_signal_count_);
769
770 fake_audio_dtls->SetDtlsState(DtlsTransportState::kConnected);
771 fake_video_dtls->SetDtlsState(DtlsTransportState::kConnected);
772 // Set the connection count to be 1 and the cricket::FakeIceTransport will set
773 // the transport state to be STATE_COMPLETED.
774 fake_video_dtls->fake_ice_transport()->SetTransportState(
775 IceTransportState::kCompleted,
776 cricket::IceTransportState::STATE_COMPLETED);
777 fake_video_dtls->SetWritable(true);
778 EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
779 EXPECT_EQ(3, connection_state_signal_count_);
780 EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionCompleted,
781 ice_connection_state_, kTimeout);
782 EXPECT_EQ(3, ice_connection_state_signal_count_);
783 EXPECT_EQ_WAIT(PeerConnectionInterface::PeerConnectionState::kConnected,
784 combined_connection_state_, kTimeout);
785 EXPECT_EQ(3, combined_connection_state_signal_count_);
786 }
787
TEST_F(JsepTransportControllerTest,SignalIceGatheringStateGathering)788 TEST_F(JsepTransportControllerTest, SignalIceGatheringStateGathering) {
789 CreateJsepTransportController(JsepTransportController::Config());
790 auto description = CreateSessionDescriptionWithoutBundle();
791 EXPECT_TRUE(transport_controller_
792 ->SetLocalDescription(SdpType::kOffer, description.get())
793 .ok());
794
795 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
796 transport_controller_->GetDtlsTransport(kAudioMid1));
797 fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
798 // Should be in the gathering state as soon as any transport starts gathering.
799 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
800 EXPECT_EQ(1, gathering_state_signal_count_);
801 }
802
TEST_F(JsepTransportControllerTest,SignalIceGatheringStateComplete)803 TEST_F(JsepTransportControllerTest, SignalIceGatheringStateComplete) {
804 CreateJsepTransportController(JsepTransportController::Config());
805 auto description = CreateSessionDescriptionWithoutBundle();
806 EXPECT_TRUE(transport_controller_
807 ->SetLocalDescription(SdpType::kOffer, description.get())
808 .ok());
809
810 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
811 transport_controller_->GetDtlsTransport(kAudioMid1));
812 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
813 transport_controller_->GetDtlsTransport(kVideoMid1));
814
815 fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
816 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
817 EXPECT_EQ(1, gathering_state_signal_count_);
818
819 // Have one transport finish gathering, to make sure gathering
820 // completion wasn't signalled if only one transport finished gathering.
821 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
822 EXPECT_EQ(1, gathering_state_signal_count_);
823
824 fake_video_dtls->fake_ice_transport()->MaybeStartGathering();
825 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
826 EXPECT_EQ(1, gathering_state_signal_count_);
827
828 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
829 EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
830 EXPECT_EQ(2, gathering_state_signal_count_);
831 }
832
833 // Test that when the last transport that hasn't finished connecting and/or
834 // gathering is destroyed, the aggregate state jumps to "completed". This can
835 // happen if, for example, we have an audio and video transport, the audio
836 // transport completes, then we start bundling video on the audio transport.
TEST_F(JsepTransportControllerTest,SignalingWhenLastIncompleteTransportDestroyed)837 TEST_F(JsepTransportControllerTest,
838 SignalingWhenLastIncompleteTransportDestroyed) {
839 CreateJsepTransportController(JsepTransportController::Config());
840 auto description = CreateSessionDescriptionWithBundleGroup();
841 EXPECT_TRUE(transport_controller_
842 ->SetLocalDescription(SdpType::kOffer, description.get())
843 .ok());
844
845 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
846 transport_controller_->GetDtlsTransport(kAudioMid1));
847 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
848 transport_controller_->GetDtlsTransport(kVideoMid1));
849 EXPECT_NE(fake_audio_dtls, fake_video_dtls);
850
851 fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
852 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
853 EXPECT_EQ(1, gathering_state_signal_count_);
854
855 // Let the audio transport complete.
856 fake_audio_dtls->SetWritable(true);
857 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
858 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
859 fake_audio_dtls->SetDtlsState(DtlsTransportState::kConnected);
860 EXPECT_EQ(1, gathering_state_signal_count_);
861
862 // Set the remote description and enable the bundle.
863 EXPECT_TRUE(transport_controller_
864 ->SetRemoteDescription(SdpType::kAnswer, description.get())
865 .ok());
866 // The BUNDLE should be enabled, the incomplete video transport should be
867 // deleted and the states shoud be updated.
868 fake_video_dtls = static_cast<FakeDtlsTransport*>(
869 transport_controller_->GetDtlsTransport(kVideoMid1));
870 EXPECT_EQ(fake_audio_dtls, fake_video_dtls);
871 EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
872 EXPECT_EQ(PeerConnectionInterface::kIceConnectionCompleted,
873 ice_connection_state_);
874 EXPECT_EQ(PeerConnectionInterface::PeerConnectionState::kConnected,
875 combined_connection_state_);
876 EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
877 EXPECT_EQ(2, gathering_state_signal_count_);
878 }
879
880 // Test that states immediately return to "new" if all transports are
881 // discarded. This should happen at offer time, even though the transport
882 // controller may keep the transport alive in case of rollback.
TEST_F(JsepTransportControllerTest,IceStatesReturnToNewWhenTransportsDiscarded)883 TEST_F(JsepTransportControllerTest,
884 IceStatesReturnToNewWhenTransportsDiscarded) {
885 CreateJsepTransportController(JsepTransportController::Config());
886 auto description = std::make_unique<cricket::SessionDescription>();
887 AddAudioSection(description.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
888 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
889 nullptr);
890 EXPECT_TRUE(transport_controller_
891 ->SetLocalDescription(SdpType::kOffer, description.get())
892 .ok());
893 EXPECT_TRUE(transport_controller_
894 ->SetRemoteDescription(SdpType::kAnswer, description.get())
895 .ok());
896
897 // Trigger and verify initial non-new states.
898 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
899 transport_controller_->GetDtlsTransport(kAudioMid1));
900 fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
901 fake_audio_dtls->fake_ice_transport()->SetTransportState(
902 webrtc::IceTransportState::kChecking,
903 cricket::IceTransportState::STATE_CONNECTING);
904 EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionChecking,
905 ice_connection_state_, kTimeout);
906 EXPECT_EQ(1, ice_connection_state_signal_count_);
907 EXPECT_EQ_WAIT(PeerConnectionInterface::PeerConnectionState::kConnecting,
908 combined_connection_state_, kTimeout);
909 EXPECT_EQ(1, combined_connection_state_signal_count_);
910 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
911 EXPECT_EQ(1, gathering_state_signal_count_);
912
913 // Reject m= section which should disconnect the transport and return states
914 // to "new".
915 description->contents()[0].rejected = true;
916 EXPECT_TRUE(transport_controller_
917 ->SetRemoteDescription(SdpType::kOffer, description.get())
918 .ok());
919 EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionNew,
920 ice_connection_state_, kTimeout);
921 EXPECT_EQ(2, ice_connection_state_signal_count_);
922 EXPECT_EQ_WAIT(PeerConnectionInterface::PeerConnectionState::kNew,
923 combined_connection_state_, kTimeout);
924 EXPECT_EQ(2, combined_connection_state_signal_count_);
925 EXPECT_EQ_WAIT(cricket::kIceGatheringNew, gathering_state_, kTimeout);
926 EXPECT_EQ(2, gathering_state_signal_count_);
927
928 // For good measure, rollback the offer and verify that states return to
929 // their previous values.
930 EXPECT_TRUE(transport_controller_->RollbackTransports().ok());
931 EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionChecking,
932 ice_connection_state_, kTimeout);
933 EXPECT_EQ(3, ice_connection_state_signal_count_);
934 EXPECT_EQ_WAIT(PeerConnectionInterface::PeerConnectionState::kConnecting,
935 combined_connection_state_, kTimeout);
936 EXPECT_EQ(3, combined_connection_state_signal_count_);
937 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
938 EXPECT_EQ(3, gathering_state_signal_count_);
939 }
940
TEST_F(JsepTransportControllerTest,SignalCandidatesGathered)941 TEST_F(JsepTransportControllerTest, SignalCandidatesGathered) {
942 CreateJsepTransportController(JsepTransportController::Config());
943 auto description = CreateSessionDescriptionWithBundleGroup();
944 EXPECT_TRUE(transport_controller_
945 ->SetLocalDescription(SdpType::kOffer, description.get())
946 .ok());
947 transport_controller_->MaybeStartGathering();
948
949 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
950 transport_controller_->GetDtlsTransport(kAudioMid1));
951 fake_audio_dtls->fake_ice_transport()->SignalCandidateGathered(
952 fake_audio_dtls->fake_ice_transport(), CreateCandidate(kAudioMid1, 1));
953 EXPECT_EQ_WAIT(1, candidates_signal_count_, kTimeout);
954 EXPECT_EQ(1u, candidates_[kAudioMid1].size());
955 }
956
TEST_F(JsepTransportControllerTest,IceSignalingOccursOnNetworkThread)957 TEST_F(JsepTransportControllerTest, IceSignalingOccursOnNetworkThread) {
958 network_thread_ = rtc::Thread::CreateWithSocketServer();
959 network_thread_->Start();
960 EXPECT_EQ(ice_signaled_on_thread_, nullptr);
961 CreateJsepTransportController(JsepTransportController::Config(),
962 network_thread_.get(),
963 /*port_allocator=*/nullptr);
964 CreateLocalDescriptionAndCompleteConnectionOnNetworkThread();
965
966 // connecting --> connected --> completed
967 EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
968 EXPECT_EQ(2, connection_state_signal_count_);
969
970 // new --> gathering --> complete
971 EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
972 EXPECT_EQ(2, gathering_state_signal_count_);
973
974 EXPECT_EQ_WAIT(1u, candidates_[kAudioMid1].size(), kTimeout);
975 EXPECT_EQ_WAIT(1u, candidates_[kVideoMid1].size(), kTimeout);
976 EXPECT_EQ(2, candidates_signal_count_);
977
978 EXPECT_EQ(ice_signaled_on_thread_, network_thread_.get());
979
980 SendTask(network_thread_.get(), [&] { transport_controller_.reset(); });
981 }
982
983 // Test that if the TransportController was created with the
984 // `redetermine_role_on_ice_restart` parameter set to false, the role is *not*
985 // redetermined on an ICE restart.
TEST_F(JsepTransportControllerTest,IceRoleNotRedetermined)986 TEST_F(JsepTransportControllerTest, IceRoleNotRedetermined) {
987 JsepTransportController::Config config;
988 config.redetermine_role_on_ice_restart = false;
989
990 CreateJsepTransportController(config);
991 // Let the `transport_controller_` be the controlled side initially.
992 auto remote_offer = std::make_unique<cricket::SessionDescription>();
993 AddAudioSection(remote_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
994 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
995 nullptr);
996 auto local_answer = std::make_unique<cricket::SessionDescription>();
997 AddAudioSection(local_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
998 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
999 nullptr);
1000
1001 EXPECT_TRUE(transport_controller_
1002 ->SetRemoteDescription(SdpType::kOffer, remote_offer.get())
1003 .ok());
1004 EXPECT_TRUE(transport_controller_
1005 ->SetLocalDescription(SdpType::kAnswer, local_answer.get())
1006 .ok());
1007
1008 auto fake_dtls = static_cast<FakeDtlsTransport*>(
1009 transport_controller_->GetDtlsTransport(kAudioMid1));
1010 EXPECT_EQ(cricket::ICEROLE_CONTROLLED,
1011 fake_dtls->fake_ice_transport()->GetIceRole());
1012
1013 // New offer will trigger the ICE restart.
1014 auto restart_local_offer = std::make_unique<cricket::SessionDescription>();
1015 AddAudioSection(restart_local_offer.get(), kAudioMid1, kIceUfrag3, kIcePwd3,
1016 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1017 nullptr);
1018 EXPECT_TRUE(
1019 transport_controller_
1020 ->SetLocalDescription(SdpType::kOffer, restart_local_offer.get())
1021 .ok());
1022 EXPECT_EQ(cricket::ICEROLE_CONTROLLED,
1023 fake_dtls->fake_ice_transport()->GetIceRole());
1024 }
1025
1026 // Tests ICE-Lite mode in remote answer.
TEST_F(JsepTransportControllerTest,SetIceRoleWhenIceLiteInRemoteAnswer)1027 TEST_F(JsepTransportControllerTest, SetIceRoleWhenIceLiteInRemoteAnswer) {
1028 CreateJsepTransportController(JsepTransportController::Config());
1029 auto local_offer = std::make_unique<cricket::SessionDescription>();
1030 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1031 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1032 nullptr);
1033 EXPECT_TRUE(transport_controller_
1034 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1035 .ok());
1036 auto fake_dtls = static_cast<FakeDtlsTransport*>(
1037 transport_controller_->GetDtlsTransport(kAudioMid1));
1038 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
1039 fake_dtls->fake_ice_transport()->GetIceRole());
1040 EXPECT_EQ(cricket::ICEMODE_FULL,
1041 fake_dtls->fake_ice_transport()->remote_ice_mode());
1042
1043 auto remote_answer = std::make_unique<cricket::SessionDescription>();
1044 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1045 cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_PASSIVE,
1046 nullptr);
1047 EXPECT_TRUE(transport_controller_
1048 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1049 .ok());
1050 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
1051 fake_dtls->fake_ice_transport()->GetIceRole());
1052 EXPECT_EQ(cricket::ICEMODE_LITE,
1053 fake_dtls->fake_ice_transport()->remote_ice_mode());
1054 }
1055
1056 // Tests that the ICE role remains "controlling" if a subsequent offer that
1057 // does an ICE restart is received from an ICE lite endpoint. Regression test
1058 // for: https://crbug.com/710760
TEST_F(JsepTransportControllerTest,IceRoleIsControllingAfterIceRestartFromIceLiteEndpoint)1059 TEST_F(JsepTransportControllerTest,
1060 IceRoleIsControllingAfterIceRestartFromIceLiteEndpoint) {
1061 CreateJsepTransportController(JsepTransportController::Config());
1062 auto remote_offer = std::make_unique<cricket::SessionDescription>();
1063 AddAudioSection(remote_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1064 cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_ACTPASS,
1065 nullptr);
1066 auto local_answer = std::make_unique<cricket::SessionDescription>();
1067 AddAudioSection(local_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1068 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1069 nullptr);
1070 // Initial Offer/Answer exchange. If the remote offerer is ICE-Lite, then the
1071 // local side is the controlling.
1072 EXPECT_TRUE(transport_controller_
1073 ->SetRemoteDescription(SdpType::kOffer, remote_offer.get())
1074 .ok());
1075 EXPECT_TRUE(transport_controller_
1076 ->SetLocalDescription(SdpType::kAnswer, local_answer.get())
1077 .ok());
1078 auto fake_dtls = static_cast<FakeDtlsTransport*>(
1079 transport_controller_->GetDtlsTransport(kAudioMid1));
1080 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
1081 fake_dtls->fake_ice_transport()->GetIceRole());
1082
1083 // In the subsequence remote offer triggers an ICE restart.
1084 auto remote_offer2 = std::make_unique<cricket::SessionDescription>();
1085 AddAudioSection(remote_offer2.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1086 cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_ACTPASS,
1087 nullptr);
1088 auto local_answer2 = std::make_unique<cricket::SessionDescription>();
1089 AddAudioSection(local_answer2.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1090 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1091 nullptr);
1092 EXPECT_TRUE(transport_controller_
1093 ->SetRemoteDescription(SdpType::kOffer, remote_offer2.get())
1094 .ok());
1095 EXPECT_TRUE(transport_controller_
1096 ->SetLocalDescription(SdpType::kAnswer, local_answer2.get())
1097 .ok());
1098 fake_dtls = static_cast<FakeDtlsTransport*>(
1099 transport_controller_->GetDtlsTransport(kAudioMid1));
1100 // The local side is still the controlling role since the remote side is using
1101 // ICE-Lite.
1102 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
1103 fake_dtls->fake_ice_transport()->GetIceRole());
1104 }
1105
1106 // Tests that the SDP has more than one audio/video m= sections.
TEST_F(JsepTransportControllerTest,MultipleMediaSectionsOfSameTypeWithBundle)1107 TEST_F(JsepTransportControllerTest, MultipleMediaSectionsOfSameTypeWithBundle) {
1108 CreateJsepTransportController(JsepTransportController::Config());
1109 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1110 bundle_group.AddContentName(kAudioMid1);
1111 bundle_group.AddContentName(kAudioMid2);
1112 bundle_group.AddContentName(kVideoMid1);
1113 bundle_group.AddContentName(kDataMid1);
1114
1115 auto local_offer = std::make_unique<cricket::SessionDescription>();
1116 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1117 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1118 nullptr);
1119 AddAudioSection(local_offer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
1120 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1121 nullptr);
1122 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1123 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1124 nullptr);
1125 AddDataSection(local_offer.get(), kDataMid1,
1126 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
1127 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1128 nullptr);
1129
1130 auto remote_answer = std::make_unique<cricket::SessionDescription>();
1131 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1132 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1133 nullptr);
1134 AddAudioSection(remote_answer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
1135 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1136 nullptr);
1137 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1138 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1139 nullptr);
1140 AddDataSection(remote_answer.get(), kDataMid1,
1141 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
1142 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1143 nullptr);
1144
1145 local_offer->AddGroup(bundle_group);
1146 remote_answer->AddGroup(bundle_group);
1147
1148 EXPECT_TRUE(transport_controller_
1149 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1150 .ok());
1151 EXPECT_TRUE(transport_controller_
1152 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1153 .ok());
1154 // Verify that all the sections are bundled on kAudio1.
1155 auto transport1 = transport_controller_->GetRtpTransport(kAudioMid1);
1156 auto transport2 = transport_controller_->GetRtpTransport(kAudioMid2);
1157 auto transport3 = transport_controller_->GetRtpTransport(kVideoMid1);
1158 auto transport4 = transport_controller_->GetRtpTransport(kDataMid1);
1159 EXPECT_EQ(transport1, transport2);
1160 EXPECT_EQ(transport1, transport3);
1161 EXPECT_EQ(transport1, transport4);
1162
1163 EXPECT_EQ(transport_controller_->LookupDtlsTransportByMid(kAudioMid1),
1164 transport_controller_->LookupDtlsTransportByMid(kVideoMid1));
1165
1166 // Verify the OnRtpTransport/DtlsTransportChanged signals are fired correctly.
1167 auto it = changed_rtp_transport_by_mid_.find(kAudioMid2);
1168 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1169 EXPECT_EQ(transport1, it->second);
1170 it = changed_rtp_transport_by_mid_.find(kAudioMid2);
1171 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1172 EXPECT_EQ(transport1, it->second);
1173 it = changed_rtp_transport_by_mid_.find(kVideoMid1);
1174 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1175 EXPECT_EQ(transport1, it->second);
1176 // Verify the DtlsTransport for the SCTP data channel is reset correctly.
1177 auto it2 = changed_dtls_transport_by_mid_.find(kDataMid1);
1178 ASSERT_TRUE(it2 != changed_dtls_transport_by_mid_.end());
1179 }
1180
TEST_F(JsepTransportControllerTest,MultipleBundleGroups)1181 TEST_F(JsepTransportControllerTest, MultipleBundleGroups) {
1182 static const char kMid1Audio[] = "1_audio";
1183 static const char kMid2Video[] = "2_video";
1184 static const char kMid3Audio[] = "3_audio";
1185 static const char kMid4Video[] = "4_video";
1186
1187 CreateJsepTransportController(JsepTransportController::Config());
1188 cricket::ContentGroup bundle_group1(cricket::GROUP_TYPE_BUNDLE);
1189 bundle_group1.AddContentName(kMid1Audio);
1190 bundle_group1.AddContentName(kMid2Video);
1191 cricket::ContentGroup bundle_group2(cricket::GROUP_TYPE_BUNDLE);
1192 bundle_group2.AddContentName(kMid3Audio);
1193 bundle_group2.AddContentName(kMid4Video);
1194
1195 auto local_offer = std::make_unique<cricket::SessionDescription>();
1196 AddAudioSection(local_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
1197 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1198 nullptr);
1199 AddVideoSection(local_offer.get(), kMid2Video, kIceUfrag2, kIcePwd2,
1200 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1201 nullptr);
1202 AddAudioSection(local_offer.get(), kMid3Audio, kIceUfrag3, kIcePwd3,
1203 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1204 nullptr);
1205 AddVideoSection(local_offer.get(), kMid4Video, kIceUfrag4, kIcePwd4,
1206 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1207 nullptr);
1208 local_offer->AddGroup(bundle_group1);
1209 local_offer->AddGroup(bundle_group2);
1210
1211 auto remote_answer = std::make_unique<cricket::SessionDescription>();
1212 AddAudioSection(remote_answer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
1213 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1214 nullptr);
1215 AddVideoSection(remote_answer.get(), kMid2Video, kIceUfrag2, kIcePwd2,
1216 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1217 nullptr);
1218 AddAudioSection(remote_answer.get(), kMid3Audio, kIceUfrag3, kIcePwd3,
1219 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1220 nullptr);
1221 AddVideoSection(remote_answer.get(), kMid4Video, kIceUfrag4, kIcePwd4,
1222 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1223 nullptr);
1224 remote_answer->AddGroup(bundle_group1);
1225 remote_answer->AddGroup(bundle_group2);
1226
1227 EXPECT_TRUE(transport_controller_
1228 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1229 .ok());
1230 EXPECT_TRUE(transport_controller_
1231 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1232 .ok());
1233
1234 // Verify that (kMid1Audio,kMid2Video) and (kMid3Audio,kMid4Video) form two
1235 // distinct bundled groups.
1236 auto mid1_transport = transport_controller_->GetRtpTransport(kMid1Audio);
1237 auto mid2_transport = transport_controller_->GetRtpTransport(kMid2Video);
1238 auto mid3_transport = transport_controller_->GetRtpTransport(kMid3Audio);
1239 auto mid4_transport = transport_controller_->GetRtpTransport(kMid4Video);
1240 EXPECT_EQ(mid1_transport, mid2_transport);
1241 EXPECT_EQ(mid3_transport, mid4_transport);
1242 EXPECT_NE(mid1_transport, mid3_transport);
1243
1244 auto it = changed_rtp_transport_by_mid_.find(kMid1Audio);
1245 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1246 EXPECT_EQ(it->second, mid1_transport);
1247
1248 it = changed_rtp_transport_by_mid_.find(kMid2Video);
1249 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1250 EXPECT_EQ(it->second, mid2_transport);
1251
1252 it = changed_rtp_transport_by_mid_.find(kMid3Audio);
1253 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1254 EXPECT_EQ(it->second, mid3_transport);
1255
1256 it = changed_rtp_transport_by_mid_.find(kMid4Video);
1257 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1258 EXPECT_EQ(it->second, mid4_transport);
1259 }
1260
TEST_F(JsepTransportControllerTest,MultipleBundleGroupsInOfferButOnlyASingleGroupInAnswer)1261 TEST_F(JsepTransportControllerTest,
1262 MultipleBundleGroupsInOfferButOnlyASingleGroupInAnswer) {
1263 static const char kMid1Audio[] = "1_audio";
1264 static const char kMid2Video[] = "2_video";
1265 static const char kMid3Audio[] = "3_audio";
1266 static const char kMid4Video[] = "4_video";
1267
1268 CreateJsepTransportController(JsepTransportController::Config());
1269 cricket::ContentGroup bundle_group1(cricket::GROUP_TYPE_BUNDLE);
1270 bundle_group1.AddContentName(kMid1Audio);
1271 bundle_group1.AddContentName(kMid2Video);
1272 cricket::ContentGroup bundle_group2(cricket::GROUP_TYPE_BUNDLE);
1273 bundle_group2.AddContentName(kMid3Audio);
1274 bundle_group2.AddContentName(kMid4Video);
1275
1276 auto local_offer = std::make_unique<cricket::SessionDescription>();
1277 AddAudioSection(local_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
1278 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1279 nullptr);
1280 AddVideoSection(local_offer.get(), kMid2Video, kIceUfrag2, kIcePwd2,
1281 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1282 nullptr);
1283 AddAudioSection(local_offer.get(), kMid3Audio, kIceUfrag3, kIcePwd3,
1284 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1285 nullptr);
1286 AddVideoSection(local_offer.get(), kMid4Video, kIceUfrag4, kIcePwd4,
1287 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1288 nullptr);
1289 // The offer has both groups.
1290 local_offer->AddGroup(bundle_group1);
1291 local_offer->AddGroup(bundle_group2);
1292
1293 auto remote_answer = std::make_unique<cricket::SessionDescription>();
1294 AddAudioSection(remote_answer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
1295 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1296 nullptr);
1297 AddVideoSection(remote_answer.get(), kMid2Video, kIceUfrag2, kIcePwd2,
1298 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1299 nullptr);
1300 AddAudioSection(remote_answer.get(), kMid3Audio, kIceUfrag3, kIcePwd3,
1301 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1302 nullptr);
1303 AddVideoSection(remote_answer.get(), kMid4Video, kIceUfrag4, kIcePwd4,
1304 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1305 nullptr);
1306 // The answer only has a single group! This is what happens when talking to an
1307 // endpoint that does not have support for multiple BUNDLE groups.
1308 remote_answer->AddGroup(bundle_group1);
1309
1310 EXPECT_TRUE(transport_controller_
1311 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1312 .ok());
1313 EXPECT_TRUE(transport_controller_
1314 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1315 .ok());
1316
1317 // Verify that (kMid1Audio,kMid2Video) form a bundle group, but that
1318 // kMid3Audio and kMid4Video are unbundled.
1319 auto mid1_transport = transport_controller_->GetRtpTransport(kMid1Audio);
1320 auto mid2_transport = transport_controller_->GetRtpTransport(kMid2Video);
1321 auto mid3_transport = transport_controller_->GetRtpTransport(kMid3Audio);
1322 auto mid4_transport = transport_controller_->GetRtpTransport(kMid4Video);
1323 EXPECT_EQ(mid1_transport, mid2_transport);
1324 EXPECT_NE(mid3_transport, mid4_transport);
1325 EXPECT_NE(mid1_transport, mid3_transport);
1326 EXPECT_NE(mid1_transport, mid4_transport);
1327 }
1328
TEST_F(JsepTransportControllerTest,MultipleBundleGroupsIllegallyChangeGroup)1329 TEST_F(JsepTransportControllerTest, MultipleBundleGroupsIllegallyChangeGroup) {
1330 static const char kMid1Audio[] = "1_audio";
1331 static const char kMid2Video[] = "2_video";
1332 static const char kMid3Audio[] = "3_audio";
1333 static const char kMid4Video[] = "4_video";
1334
1335 CreateJsepTransportController(JsepTransportController::Config());
1336 // Offer groups (kMid1Audio,kMid2Video) and (kMid3Audio,kMid4Video).
1337 cricket::ContentGroup offer_bundle_group1(cricket::GROUP_TYPE_BUNDLE);
1338 offer_bundle_group1.AddContentName(kMid1Audio);
1339 offer_bundle_group1.AddContentName(kMid2Video);
1340 cricket::ContentGroup offer_bundle_group2(cricket::GROUP_TYPE_BUNDLE);
1341 offer_bundle_group2.AddContentName(kMid3Audio);
1342 offer_bundle_group2.AddContentName(kMid4Video);
1343 // Answer groups (kMid1Audio,kMid4Video) and (kMid3Audio,kMid2Video), i.e. the
1344 // second group members have switched places. This should get rejected.
1345 cricket::ContentGroup answer_bundle_group1(cricket::GROUP_TYPE_BUNDLE);
1346 answer_bundle_group1.AddContentName(kMid1Audio);
1347 answer_bundle_group1.AddContentName(kMid4Video);
1348 cricket::ContentGroup answer_bundle_group2(cricket::GROUP_TYPE_BUNDLE);
1349 answer_bundle_group2.AddContentName(kMid3Audio);
1350 answer_bundle_group2.AddContentName(kMid2Video);
1351
1352 auto local_offer = std::make_unique<cricket::SessionDescription>();
1353 AddAudioSection(local_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
1354 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1355 nullptr);
1356 AddVideoSection(local_offer.get(), kMid2Video, kIceUfrag2, kIcePwd2,
1357 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1358 nullptr);
1359 AddAudioSection(local_offer.get(), kMid3Audio, kIceUfrag3, kIcePwd3,
1360 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1361 nullptr);
1362 AddVideoSection(local_offer.get(), kMid4Video, kIceUfrag4, kIcePwd4,
1363 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1364 nullptr);
1365 local_offer->AddGroup(offer_bundle_group1);
1366 local_offer->AddGroup(offer_bundle_group2);
1367
1368 auto remote_answer = std::make_unique<cricket::SessionDescription>();
1369 AddAudioSection(remote_answer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
1370 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1371 nullptr);
1372 AddVideoSection(remote_answer.get(), kMid2Video, kIceUfrag2, kIcePwd2,
1373 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1374 nullptr);
1375 AddAudioSection(remote_answer.get(), kMid3Audio, kIceUfrag3, kIcePwd3,
1376 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1377 nullptr);
1378 AddVideoSection(remote_answer.get(), kMid4Video, kIceUfrag4, kIcePwd4,
1379 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1380 nullptr);
1381 remote_answer->AddGroup(answer_bundle_group1);
1382 remote_answer->AddGroup(answer_bundle_group2);
1383
1384 // Accept offer.
1385 EXPECT_TRUE(transport_controller_
1386 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1387 .ok());
1388 // Reject answer!
1389 EXPECT_FALSE(transport_controller_
1390 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1391 .ok());
1392 }
1393
TEST_F(JsepTransportControllerTest,MultipleBundleGroupsInvalidSubsets)1394 TEST_F(JsepTransportControllerTest, MultipleBundleGroupsInvalidSubsets) {
1395 static const char kMid1Audio[] = "1_audio";
1396 static const char kMid2Video[] = "2_video";
1397 static const char kMid3Audio[] = "3_audio";
1398 static const char kMid4Video[] = "4_video";
1399
1400 CreateJsepTransportController(JsepTransportController::Config());
1401 // Offer groups (kMid1Audio,kMid2Video) and (kMid3Audio,kMid4Video).
1402 cricket::ContentGroup offer_bundle_group1(cricket::GROUP_TYPE_BUNDLE);
1403 offer_bundle_group1.AddContentName(kMid1Audio);
1404 offer_bundle_group1.AddContentName(kMid2Video);
1405 cricket::ContentGroup offer_bundle_group2(cricket::GROUP_TYPE_BUNDLE);
1406 offer_bundle_group2.AddContentName(kMid3Audio);
1407 offer_bundle_group2.AddContentName(kMid4Video);
1408 // Answer groups (kMid1Audio) and (kMid2Video), i.e. the second group was
1409 // moved from the first group. This should get rejected.
1410 cricket::ContentGroup answer_bundle_group1(cricket::GROUP_TYPE_BUNDLE);
1411 answer_bundle_group1.AddContentName(kMid1Audio);
1412 cricket::ContentGroup answer_bundle_group2(cricket::GROUP_TYPE_BUNDLE);
1413 answer_bundle_group2.AddContentName(kMid2Video);
1414
1415 auto local_offer = std::make_unique<cricket::SessionDescription>();
1416 AddAudioSection(local_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
1417 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1418 nullptr);
1419 AddVideoSection(local_offer.get(), kMid2Video, kIceUfrag2, kIcePwd2,
1420 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1421 nullptr);
1422 AddAudioSection(local_offer.get(), kMid3Audio, kIceUfrag3, kIcePwd3,
1423 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1424 nullptr);
1425 AddVideoSection(local_offer.get(), kMid4Video, kIceUfrag4, kIcePwd4,
1426 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1427 nullptr);
1428 local_offer->AddGroup(offer_bundle_group1);
1429 local_offer->AddGroup(offer_bundle_group2);
1430
1431 auto remote_answer = std::make_unique<cricket::SessionDescription>();
1432 AddAudioSection(remote_answer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
1433 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1434 nullptr);
1435 AddVideoSection(remote_answer.get(), kMid2Video, kIceUfrag2, kIcePwd2,
1436 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1437 nullptr);
1438 AddAudioSection(remote_answer.get(), kMid3Audio, kIceUfrag3, kIcePwd3,
1439 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1440 nullptr);
1441 AddVideoSection(remote_answer.get(), kMid4Video, kIceUfrag4, kIcePwd4,
1442 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1443 nullptr);
1444 remote_answer->AddGroup(answer_bundle_group1);
1445 remote_answer->AddGroup(answer_bundle_group2);
1446
1447 // Accept offer.
1448 EXPECT_TRUE(transport_controller_
1449 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1450 .ok());
1451 // Reject answer!
1452 EXPECT_FALSE(transport_controller_
1453 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1454 .ok());
1455 }
1456
TEST_F(JsepTransportControllerTest,MultipleBundleGroupsInvalidOverlap)1457 TEST_F(JsepTransportControllerTest, MultipleBundleGroupsInvalidOverlap) {
1458 static const char kMid1Audio[] = "1_audio";
1459 static const char kMid2Video[] = "2_video";
1460 static const char kMid3Audio[] = "3_audio";
1461
1462 CreateJsepTransportController(JsepTransportController::Config());
1463 // Offer groups (kMid1Audio,kMid3Audio) and (kMid2Video,kMid3Audio), i.e.
1464 // kMid3Audio is in both groups - this is illegal.
1465 cricket::ContentGroup offer_bundle_group1(cricket::GROUP_TYPE_BUNDLE);
1466 offer_bundle_group1.AddContentName(kMid1Audio);
1467 offer_bundle_group1.AddContentName(kMid3Audio);
1468 cricket::ContentGroup offer_bundle_group2(cricket::GROUP_TYPE_BUNDLE);
1469 offer_bundle_group2.AddContentName(kMid2Video);
1470 offer_bundle_group2.AddContentName(kMid3Audio);
1471
1472 auto offer = std::make_unique<cricket::SessionDescription>();
1473 AddAudioSection(offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
1474 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1475 nullptr);
1476 AddVideoSection(offer.get(), kMid2Video, kIceUfrag2, kIcePwd2,
1477 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1478 nullptr);
1479 AddAudioSection(offer.get(), kMid3Audio, kIceUfrag3, kIcePwd3,
1480 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1481 nullptr);
1482 offer->AddGroup(offer_bundle_group1);
1483 offer->AddGroup(offer_bundle_group2);
1484
1485 // Reject offer, both if set as local or remote.
1486 EXPECT_FALSE(
1487 transport_controller_->SetLocalDescription(SdpType::kOffer, offer.get())
1488 .ok());
1489 EXPECT_FALSE(
1490 transport_controller_->SetRemoteDescription(SdpType::kOffer, offer.get())
1491 .ok());
1492 }
1493
TEST_F(JsepTransportControllerTest,MultipleBundleGroupsUnbundleFirstMid)1494 TEST_F(JsepTransportControllerTest, MultipleBundleGroupsUnbundleFirstMid) {
1495 static const char kMid1Audio[] = "1_audio";
1496 static const char kMid2Audio[] = "2_audio";
1497 static const char kMid3Audio[] = "3_audio";
1498 static const char kMid4Video[] = "4_video";
1499 static const char kMid5Video[] = "5_video";
1500 static const char kMid6Video[] = "6_video";
1501
1502 CreateJsepTransportController(JsepTransportController::Config());
1503 // Offer groups (kMid1Audio,kMid2Audio,kMid3Audio) and
1504 // (kMid4Video,kMid5Video,kMid6Video).
1505 cricket::ContentGroup offer_bundle_group1(cricket::GROUP_TYPE_BUNDLE);
1506 offer_bundle_group1.AddContentName(kMid1Audio);
1507 offer_bundle_group1.AddContentName(kMid2Audio);
1508 offer_bundle_group1.AddContentName(kMid3Audio);
1509 cricket::ContentGroup offer_bundle_group2(cricket::GROUP_TYPE_BUNDLE);
1510 offer_bundle_group2.AddContentName(kMid4Video);
1511 offer_bundle_group2.AddContentName(kMid5Video);
1512 offer_bundle_group2.AddContentName(kMid6Video);
1513 // Answer groups (kMid2Audio,kMid3Audio) and (kMid5Video,kMid6Video), i.e.
1514 // we've moved the first MIDs out of the groups.
1515 cricket::ContentGroup answer_bundle_group1(cricket::GROUP_TYPE_BUNDLE);
1516 answer_bundle_group1.AddContentName(kMid2Audio);
1517 answer_bundle_group1.AddContentName(kMid3Audio);
1518 cricket::ContentGroup answer_bundle_group2(cricket::GROUP_TYPE_BUNDLE);
1519 answer_bundle_group2.AddContentName(kMid5Video);
1520 answer_bundle_group2.AddContentName(kMid6Video);
1521
1522 auto local_offer = std::make_unique<cricket::SessionDescription>();
1523 AddAudioSection(local_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
1524 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1525 nullptr);
1526 AddAudioSection(local_offer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
1527 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1528 nullptr);
1529 AddAudioSection(local_offer.get(), kMid3Audio, kIceUfrag1, kIcePwd1,
1530 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1531 nullptr);
1532 AddVideoSection(local_offer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
1533 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1534 nullptr);
1535 AddVideoSection(local_offer.get(), kMid5Video, kIceUfrag2, kIcePwd2,
1536 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1537 nullptr);
1538 AddVideoSection(local_offer.get(), kMid6Video, kIceUfrag2, kIcePwd2,
1539 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1540 nullptr);
1541 local_offer->AddGroup(offer_bundle_group1);
1542 local_offer->AddGroup(offer_bundle_group2);
1543
1544 auto remote_answer = std::make_unique<cricket::SessionDescription>();
1545 AddAudioSection(remote_answer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
1546 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1547 nullptr);
1548 AddAudioSection(remote_answer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
1549 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1550 nullptr);
1551 AddAudioSection(remote_answer.get(), kMid3Audio, kIceUfrag1, kIcePwd1,
1552 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1553 nullptr);
1554 AddVideoSection(remote_answer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
1555 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1556 nullptr);
1557 AddVideoSection(remote_answer.get(), kMid5Video, kIceUfrag2, kIcePwd2,
1558 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1559 nullptr);
1560 AddVideoSection(remote_answer.get(), kMid6Video, kIceUfrag2, kIcePwd2,
1561 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1562 nullptr);
1563 remote_answer->AddGroup(answer_bundle_group1);
1564 remote_answer->AddGroup(answer_bundle_group2);
1565
1566 EXPECT_TRUE(transport_controller_
1567 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1568 .ok());
1569 EXPECT_TRUE(transport_controller_
1570 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1571 .ok());
1572
1573 auto mid1_transport = transport_controller_->GetRtpTransport(kMid1Audio);
1574 auto mid2_transport = transport_controller_->GetRtpTransport(kMid2Audio);
1575 auto mid3_transport = transport_controller_->GetRtpTransport(kMid3Audio);
1576 auto mid4_transport = transport_controller_->GetRtpTransport(kMid4Video);
1577 auto mid5_transport = transport_controller_->GetRtpTransport(kMid5Video);
1578 auto mid6_transport = transport_controller_->GetRtpTransport(kMid6Video);
1579 EXPECT_NE(mid1_transport, mid2_transport);
1580 EXPECT_EQ(mid2_transport, mid3_transport);
1581 EXPECT_NE(mid4_transport, mid5_transport);
1582 EXPECT_EQ(mid5_transport, mid6_transport);
1583 EXPECT_NE(mid1_transport, mid4_transport);
1584 EXPECT_NE(mid2_transport, mid5_transport);
1585 }
1586
TEST_F(JsepTransportControllerTest,MultipleBundleGroupsChangeFirstMid)1587 TEST_F(JsepTransportControllerTest, MultipleBundleGroupsChangeFirstMid) {
1588 static const char kMid1Audio[] = "1_audio";
1589 static const char kMid2Audio[] = "2_audio";
1590 static const char kMid3Audio[] = "3_audio";
1591 static const char kMid4Video[] = "4_video";
1592 static const char kMid5Video[] = "5_video";
1593 static const char kMid6Video[] = "6_video";
1594
1595 CreateJsepTransportController(JsepTransportController::Config());
1596 // Offer groups (kMid1Audio,kMid2Audio,kMid3Audio) and
1597 // (kMid4Video,kMid5Video,kMid6Video).
1598 cricket::ContentGroup offer_bundle_group1(cricket::GROUP_TYPE_BUNDLE);
1599 offer_bundle_group1.AddContentName(kMid1Audio);
1600 offer_bundle_group1.AddContentName(kMid2Audio);
1601 offer_bundle_group1.AddContentName(kMid3Audio);
1602 cricket::ContentGroup offer_bundle_group2(cricket::GROUP_TYPE_BUNDLE);
1603 offer_bundle_group2.AddContentName(kMid4Video);
1604 offer_bundle_group2.AddContentName(kMid5Video);
1605 offer_bundle_group2.AddContentName(kMid6Video);
1606 // Answer groups (kMid2Audio,kMid1Audio,kMid3Audio) and
1607 // (kMid5Video,kMid6Video,kMid4Video), i.e. we've changed which MID is first
1608 // but accept the whole group.
1609 cricket::ContentGroup answer_bundle_group1(cricket::GROUP_TYPE_BUNDLE);
1610 answer_bundle_group1.AddContentName(kMid2Audio);
1611 answer_bundle_group1.AddContentName(kMid1Audio);
1612 answer_bundle_group1.AddContentName(kMid3Audio);
1613 cricket::ContentGroup answer_bundle_group2(cricket::GROUP_TYPE_BUNDLE);
1614 answer_bundle_group2.AddContentName(kMid5Video);
1615 answer_bundle_group2.AddContentName(kMid6Video);
1616 answer_bundle_group2.AddContentName(kMid4Video);
1617
1618 auto local_offer = std::make_unique<cricket::SessionDescription>();
1619 AddAudioSection(local_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
1620 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1621 nullptr);
1622 AddAudioSection(local_offer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
1623 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1624 nullptr);
1625 AddAudioSection(local_offer.get(), kMid3Audio, kIceUfrag1, kIcePwd1,
1626 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1627 nullptr);
1628 AddVideoSection(local_offer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
1629 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1630 nullptr);
1631 AddVideoSection(local_offer.get(), kMid5Video, kIceUfrag2, kIcePwd2,
1632 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1633 nullptr);
1634 AddVideoSection(local_offer.get(), kMid6Video, kIceUfrag2, kIcePwd2,
1635 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1636 nullptr);
1637 local_offer->AddGroup(offer_bundle_group1);
1638 local_offer->AddGroup(offer_bundle_group2);
1639
1640 auto remote_answer = std::make_unique<cricket::SessionDescription>();
1641 AddAudioSection(remote_answer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
1642 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1643 nullptr);
1644 AddAudioSection(remote_answer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
1645 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1646 nullptr);
1647 AddAudioSection(remote_answer.get(), kMid3Audio, kIceUfrag1, kIcePwd1,
1648 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1649 nullptr);
1650 AddVideoSection(remote_answer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
1651 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1652 nullptr);
1653 AddVideoSection(remote_answer.get(), kMid5Video, kIceUfrag2, kIcePwd2,
1654 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1655 nullptr);
1656 AddVideoSection(remote_answer.get(), kMid6Video, kIceUfrag2, kIcePwd2,
1657 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1658 nullptr);
1659 remote_answer->AddGroup(answer_bundle_group1);
1660 remote_answer->AddGroup(answer_bundle_group2);
1661
1662 EXPECT_TRUE(transport_controller_
1663 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1664 .ok());
1665
1666 // The fact that we accept this answer is actually a bug. If we accept the
1667 // first MID to be in the group, we should also accept that it is the tagged
1668 // one.
1669 // TODO(https://crbug.com/webrtc/12699): When this issue is fixed, change this
1670 // to EXPECT_FALSE and remove the below expectations about transports.
1671 EXPECT_TRUE(transport_controller_
1672 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1673 .ok());
1674 auto mid1_transport = transport_controller_->GetRtpTransport(kMid1Audio);
1675 auto mid2_transport = transport_controller_->GetRtpTransport(kMid2Audio);
1676 auto mid3_transport = transport_controller_->GetRtpTransport(kMid3Audio);
1677 auto mid4_transport = transport_controller_->GetRtpTransport(kMid4Video);
1678 auto mid5_transport = transport_controller_->GetRtpTransport(kMid5Video);
1679 auto mid6_transport = transport_controller_->GetRtpTransport(kMid6Video);
1680 EXPECT_NE(mid1_transport, mid4_transport);
1681 EXPECT_EQ(mid1_transport, mid2_transport);
1682 EXPECT_EQ(mid2_transport, mid3_transport);
1683 EXPECT_EQ(mid4_transport, mid5_transport);
1684 EXPECT_EQ(mid5_transport, mid6_transport);
1685 }
1686
TEST_F(JsepTransportControllerTest,MultipleBundleGroupsSectionsAddedInSubsequentOffer)1687 TEST_F(JsepTransportControllerTest,
1688 MultipleBundleGroupsSectionsAddedInSubsequentOffer) {
1689 static const char kMid1Audio[] = "1_audio";
1690 static const char kMid2Audio[] = "2_audio";
1691 static const char kMid3Audio[] = "3_audio";
1692 static const char kMid4Video[] = "4_video";
1693 static const char kMid5Video[] = "5_video";
1694 static const char kMid6Video[] = "6_video";
1695
1696 CreateJsepTransportController(JsepTransportController::Config());
1697 // Start by grouping (kMid1Audio,kMid2Audio) and (kMid4Video,kMid4f5Video).
1698 cricket::ContentGroup bundle_group1(cricket::GROUP_TYPE_BUNDLE);
1699 bundle_group1.AddContentName(kMid1Audio);
1700 bundle_group1.AddContentName(kMid2Audio);
1701 cricket::ContentGroup bundle_group2(cricket::GROUP_TYPE_BUNDLE);
1702 bundle_group2.AddContentName(kMid4Video);
1703 bundle_group2.AddContentName(kMid5Video);
1704
1705 auto local_offer = std::make_unique<cricket::SessionDescription>();
1706 AddAudioSection(local_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
1707 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1708 nullptr);
1709 AddAudioSection(local_offer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
1710 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1711 nullptr);
1712 AddVideoSection(local_offer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
1713 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1714 nullptr);
1715 AddVideoSection(local_offer.get(), kMid5Video, kIceUfrag2, kIcePwd2,
1716 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1717 nullptr);
1718 local_offer->AddGroup(bundle_group1);
1719 local_offer->AddGroup(bundle_group2);
1720
1721 auto remote_answer = std::make_unique<cricket::SessionDescription>();
1722 AddAudioSection(remote_answer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
1723 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1724 nullptr);
1725 AddAudioSection(remote_answer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
1726 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1727 nullptr);
1728 AddVideoSection(remote_answer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
1729 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1730 nullptr);
1731 AddVideoSection(remote_answer.get(), kMid5Video, kIceUfrag2, kIcePwd2,
1732 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1733 nullptr);
1734 remote_answer->AddGroup(bundle_group1);
1735 remote_answer->AddGroup(bundle_group2);
1736
1737 EXPECT_TRUE(transport_controller_
1738 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1739 .ok());
1740 EXPECT_TRUE(transport_controller_
1741 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1742 .ok());
1743
1744 // Add kMid3Audio and kMid6Video to the respective audio/video bundle groups.
1745 cricket::ContentGroup new_bundle_group1(cricket::GROUP_TYPE_BUNDLE);
1746 bundle_group1.AddContentName(kMid3Audio);
1747 cricket::ContentGroup new_bundle_group2(cricket::GROUP_TYPE_BUNDLE);
1748 bundle_group2.AddContentName(kMid6Video);
1749
1750 auto subsequent_offer = std::make_unique<cricket::SessionDescription>();
1751 AddAudioSection(subsequent_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
1752 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1753 nullptr);
1754 AddAudioSection(subsequent_offer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
1755 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1756 nullptr);
1757 AddAudioSection(subsequent_offer.get(), kMid3Audio, kIceUfrag1, kIcePwd1,
1758 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1759 nullptr);
1760 AddVideoSection(subsequent_offer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
1761 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1762 nullptr);
1763 AddVideoSection(subsequent_offer.get(), kMid5Video, kIceUfrag2, kIcePwd2,
1764 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1765 nullptr);
1766 AddVideoSection(subsequent_offer.get(), kMid6Video, kIceUfrag2, kIcePwd2,
1767 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1768 nullptr);
1769 subsequent_offer->AddGroup(bundle_group1);
1770 subsequent_offer->AddGroup(bundle_group2);
1771 EXPECT_TRUE(transport_controller_
1772 ->SetLocalDescription(SdpType::kOffer, subsequent_offer.get())
1773 .ok());
1774 auto mid1_transport = transport_controller_->GetRtpTransport(kMid1Audio);
1775 auto mid2_transport = transport_controller_->GetRtpTransport(kMid2Audio);
1776 auto mid3_transport = transport_controller_->GetRtpTransport(kMid3Audio);
1777 auto mid4_transport = transport_controller_->GetRtpTransport(kMid4Video);
1778 auto mid5_transport = transport_controller_->GetRtpTransport(kMid5Video);
1779 auto mid6_transport = transport_controller_->GetRtpTransport(kMid6Video);
1780 EXPECT_NE(mid1_transport, mid4_transport);
1781 EXPECT_EQ(mid1_transport, mid2_transport);
1782 EXPECT_EQ(mid2_transport, mid3_transport);
1783 EXPECT_EQ(mid4_transport, mid5_transport);
1784 EXPECT_EQ(mid5_transport, mid6_transport);
1785 }
1786
TEST_F(JsepTransportControllerTest,MultipleBundleGroupsCombinedInSubsequentOffer)1787 TEST_F(JsepTransportControllerTest,
1788 MultipleBundleGroupsCombinedInSubsequentOffer) {
1789 static const char kMid1Audio[] = "1_audio";
1790 static const char kMid2Audio[] = "2_audio";
1791 static const char kMid3Video[] = "3_video";
1792 static const char kMid4Video[] = "4_video";
1793
1794 CreateJsepTransportController(JsepTransportController::Config());
1795 // Start by grouping (kMid1Audio,kMid2Audio) and (kMid3Video,kMid4Video).
1796 cricket::ContentGroup bundle_group1(cricket::GROUP_TYPE_BUNDLE);
1797 bundle_group1.AddContentName(kMid1Audio);
1798 bundle_group1.AddContentName(kMid2Audio);
1799 cricket::ContentGroup bundle_group2(cricket::GROUP_TYPE_BUNDLE);
1800 bundle_group2.AddContentName(kMid3Video);
1801 bundle_group2.AddContentName(kMid4Video);
1802
1803 auto local_offer = std::make_unique<cricket::SessionDescription>();
1804 AddAudioSection(local_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
1805 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1806 nullptr);
1807 AddAudioSection(local_offer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
1808 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1809 nullptr);
1810 AddVideoSection(local_offer.get(), kMid3Video, kIceUfrag2, kIcePwd2,
1811 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1812 nullptr);
1813 AddVideoSection(local_offer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
1814 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1815 nullptr);
1816 local_offer->AddGroup(bundle_group1);
1817 local_offer->AddGroup(bundle_group2);
1818
1819 auto remote_answer = std::make_unique<cricket::SessionDescription>();
1820 AddAudioSection(remote_answer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
1821 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1822 nullptr);
1823 AddAudioSection(remote_answer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
1824 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1825 nullptr);
1826 AddVideoSection(remote_answer.get(), kMid3Video, kIceUfrag2, kIcePwd2,
1827 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1828 nullptr);
1829 AddVideoSection(remote_answer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
1830 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1831 nullptr);
1832 remote_answer->AddGroup(bundle_group1);
1833 remote_answer->AddGroup(bundle_group2);
1834
1835 EXPECT_TRUE(transport_controller_
1836 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1837 .ok());
1838 EXPECT_TRUE(transport_controller_
1839 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1840 .ok());
1841
1842 // Switch to grouping (kMid1Audio,kMid2Audio,kMid3Video,kMid4Video).
1843 // This is a illegal without first removing m= sections from their groups.
1844 cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
1845 new_bundle_group.AddContentName(kMid1Audio);
1846 new_bundle_group.AddContentName(kMid2Audio);
1847 new_bundle_group.AddContentName(kMid3Video);
1848 new_bundle_group.AddContentName(kMid4Video);
1849
1850 auto subsequent_offer = std::make_unique<cricket::SessionDescription>();
1851 AddAudioSection(subsequent_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
1852 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1853 nullptr);
1854 AddAudioSection(subsequent_offer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
1855 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1856 nullptr);
1857 AddVideoSection(subsequent_offer.get(), kMid3Video, kIceUfrag2, kIcePwd2,
1858 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1859 nullptr);
1860 AddVideoSection(subsequent_offer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
1861 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1862 nullptr);
1863 subsequent_offer->AddGroup(new_bundle_group);
1864 EXPECT_FALSE(
1865 transport_controller_
1866 ->SetLocalDescription(SdpType::kOffer, subsequent_offer.get())
1867 .ok());
1868 }
1869
TEST_F(JsepTransportControllerTest,MultipleBundleGroupsSplitInSubsequentOffer)1870 TEST_F(JsepTransportControllerTest,
1871 MultipleBundleGroupsSplitInSubsequentOffer) {
1872 static const char kMid1Audio[] = "1_audio";
1873 static const char kMid2Audio[] = "2_audio";
1874 static const char kMid3Video[] = "3_video";
1875 static const char kMid4Video[] = "4_video";
1876
1877 CreateJsepTransportController(JsepTransportController::Config());
1878 // Start by grouping (kMid1Audio,kMid2Audio,kMid3Video,kMid4Video).
1879 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1880 bundle_group.AddContentName(kMid1Audio);
1881 bundle_group.AddContentName(kMid2Audio);
1882 bundle_group.AddContentName(kMid3Video);
1883 bundle_group.AddContentName(kMid4Video);
1884
1885 auto local_offer = std::make_unique<cricket::SessionDescription>();
1886 AddAudioSection(local_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
1887 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1888 nullptr);
1889 AddAudioSection(local_offer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
1890 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1891 nullptr);
1892 AddVideoSection(local_offer.get(), kMid3Video, kIceUfrag2, kIcePwd2,
1893 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1894 nullptr);
1895 AddVideoSection(local_offer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
1896 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1897 nullptr);
1898 local_offer->AddGroup(bundle_group);
1899
1900 auto remote_answer = std::make_unique<cricket::SessionDescription>();
1901 AddAudioSection(remote_answer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
1902 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1903 nullptr);
1904 AddAudioSection(remote_answer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
1905 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1906 nullptr);
1907 AddVideoSection(remote_answer.get(), kMid3Video, kIceUfrag2, kIcePwd2,
1908 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1909 nullptr);
1910 AddVideoSection(remote_answer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
1911 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1912 nullptr);
1913 remote_answer->AddGroup(bundle_group);
1914
1915 EXPECT_TRUE(transport_controller_
1916 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1917 .ok());
1918 EXPECT_TRUE(transport_controller_
1919 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1920 .ok());
1921
1922 // Switch to grouping (kMid1Audio,kMid2Audio) and (kMid3Video,kMid4Video).
1923 // This is a illegal without first removing m= sections from their groups.
1924 cricket::ContentGroup new_bundle_group1(cricket::GROUP_TYPE_BUNDLE);
1925 new_bundle_group1.AddContentName(kMid1Audio);
1926 new_bundle_group1.AddContentName(kMid2Audio);
1927 cricket::ContentGroup new_bundle_group2(cricket::GROUP_TYPE_BUNDLE);
1928 new_bundle_group2.AddContentName(kMid3Video);
1929 new_bundle_group2.AddContentName(kMid4Video);
1930
1931 auto subsequent_offer = std::make_unique<cricket::SessionDescription>();
1932 AddAudioSection(subsequent_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
1933 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1934 nullptr);
1935 AddAudioSection(subsequent_offer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
1936 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1937 nullptr);
1938 AddVideoSection(subsequent_offer.get(), kMid3Video, kIceUfrag2, kIcePwd2,
1939 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1940 nullptr);
1941 AddVideoSection(subsequent_offer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
1942 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1943 nullptr);
1944 subsequent_offer->AddGroup(new_bundle_group1);
1945 subsequent_offer->AddGroup(new_bundle_group2);
1946 EXPECT_FALSE(
1947 transport_controller_
1948 ->SetLocalDescription(SdpType::kOffer, subsequent_offer.get())
1949 .ok());
1950 }
1951
TEST_F(JsepTransportControllerTest,MultipleBundleGroupsShuffledInSubsequentOffer)1952 TEST_F(JsepTransportControllerTest,
1953 MultipleBundleGroupsShuffledInSubsequentOffer) {
1954 static const char kMid1Audio[] = "1_audio";
1955 static const char kMid2Audio[] = "2_audio";
1956 static const char kMid3Video[] = "3_video";
1957 static const char kMid4Video[] = "4_video";
1958
1959 CreateJsepTransportController(JsepTransportController::Config());
1960 // Start by grouping (kMid1Audio,kMid2Audio) and (kMid3Video,kMid4Video).
1961 cricket::ContentGroup bundle_group1(cricket::GROUP_TYPE_BUNDLE);
1962 bundle_group1.AddContentName(kMid1Audio);
1963 bundle_group1.AddContentName(kMid2Audio);
1964 cricket::ContentGroup bundle_group2(cricket::GROUP_TYPE_BUNDLE);
1965 bundle_group2.AddContentName(kMid3Video);
1966 bundle_group2.AddContentName(kMid4Video);
1967
1968 auto local_offer = std::make_unique<cricket::SessionDescription>();
1969 AddAudioSection(local_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
1970 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1971 nullptr);
1972 AddAudioSection(local_offer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
1973 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1974 nullptr);
1975 AddVideoSection(local_offer.get(), kMid3Video, kIceUfrag2, kIcePwd2,
1976 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1977 nullptr);
1978 AddVideoSection(local_offer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
1979 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1980 nullptr);
1981 local_offer->AddGroup(bundle_group1);
1982 local_offer->AddGroup(bundle_group2);
1983
1984 auto remote_answer = std::make_unique<cricket::SessionDescription>();
1985 AddAudioSection(remote_answer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
1986 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1987 nullptr);
1988 AddAudioSection(remote_answer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
1989 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1990 nullptr);
1991 AddVideoSection(remote_answer.get(), kMid3Video, kIceUfrag2, kIcePwd2,
1992 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1993 nullptr);
1994 AddVideoSection(remote_answer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
1995 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1996 nullptr);
1997 remote_answer->AddGroup(bundle_group1);
1998 remote_answer->AddGroup(bundle_group2);
1999
2000 EXPECT_TRUE(transport_controller_
2001 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
2002 .ok());
2003 EXPECT_TRUE(transport_controller_
2004 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
2005 .ok());
2006
2007 // Switch to grouping (kMid1Audio,kMid3Video) and (kMid2Audio,kMid3Video).
2008 // This is a illegal without first removing m= sections from their groups.
2009 cricket::ContentGroup new_bundle_group1(cricket::GROUP_TYPE_BUNDLE);
2010 new_bundle_group1.AddContentName(kMid1Audio);
2011 new_bundle_group1.AddContentName(kMid3Video);
2012 cricket::ContentGroup new_bundle_group2(cricket::GROUP_TYPE_BUNDLE);
2013 new_bundle_group2.AddContentName(kMid2Audio);
2014 new_bundle_group2.AddContentName(kMid4Video);
2015
2016 auto subsequent_offer = std::make_unique<cricket::SessionDescription>();
2017 AddAudioSection(subsequent_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
2018 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2019 nullptr);
2020 AddAudioSection(subsequent_offer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
2021 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2022 nullptr);
2023 AddVideoSection(subsequent_offer.get(), kMid3Video, kIceUfrag2, kIcePwd2,
2024 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2025 nullptr);
2026 AddVideoSection(subsequent_offer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
2027 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2028 nullptr);
2029 subsequent_offer->AddGroup(new_bundle_group1);
2030 subsequent_offer->AddGroup(new_bundle_group2);
2031 EXPECT_FALSE(
2032 transport_controller_
2033 ->SetLocalDescription(SdpType::kOffer, subsequent_offer.get())
2034 .ok());
2035 }
2036
2037 // Tests that only a subset of all the m= sections are bundled.
TEST_F(JsepTransportControllerTest,BundleSubsetOfMediaSections)2038 TEST_F(JsepTransportControllerTest, BundleSubsetOfMediaSections) {
2039 CreateJsepTransportController(JsepTransportController::Config());
2040 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
2041 bundle_group.AddContentName(kAudioMid1);
2042 bundle_group.AddContentName(kVideoMid1);
2043
2044 auto local_offer = std::make_unique<cricket::SessionDescription>();
2045 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
2046 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2047 nullptr);
2048 AddAudioSection(local_offer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
2049 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2050 nullptr);
2051 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
2052 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2053 nullptr);
2054
2055 auto remote_answer = std::make_unique<cricket::SessionDescription>();
2056 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
2057 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
2058 nullptr);
2059 AddAudioSection(remote_answer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
2060 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
2061 nullptr);
2062 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
2063 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
2064 nullptr);
2065
2066 local_offer->AddGroup(bundle_group);
2067 remote_answer->AddGroup(bundle_group);
2068 EXPECT_TRUE(transport_controller_
2069 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
2070 .ok());
2071 EXPECT_TRUE(transport_controller_
2072 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
2073 .ok());
2074
2075 // Verifiy that only `kAudio1` and `kVideo1` are bundled.
2076 auto transport1 = transport_controller_->GetRtpTransport(kAudioMid1);
2077 auto transport2 = transport_controller_->GetRtpTransport(kAudioMid2);
2078 auto transport3 = transport_controller_->GetRtpTransport(kVideoMid1);
2079 EXPECT_NE(transport1, transport2);
2080 EXPECT_EQ(transport1, transport3);
2081
2082 auto it = changed_rtp_transport_by_mid_.find(kVideoMid1);
2083 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
2084 EXPECT_EQ(transport1, it->second);
2085 it = changed_rtp_transport_by_mid_.find(kAudioMid2);
2086 EXPECT_TRUE(transport2 == it->second);
2087 }
2088
2089 // Tests that the initial offer/answer only have data section and audio/video
2090 // sections are added in the subsequent offer.
TEST_F(JsepTransportControllerTest,BundleOnDataSectionInSubsequentOffer)2091 TEST_F(JsepTransportControllerTest, BundleOnDataSectionInSubsequentOffer) {
2092 CreateJsepTransportController(JsepTransportController::Config());
2093 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
2094 bundle_group.AddContentName(kDataMid1);
2095
2096 auto local_offer = std::make_unique<cricket::SessionDescription>();
2097 AddDataSection(local_offer.get(), kDataMid1,
2098 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
2099 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2100 nullptr);
2101 auto remote_answer = std::make_unique<cricket::SessionDescription>();
2102 AddDataSection(remote_answer.get(), kDataMid1,
2103 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
2104 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
2105 nullptr);
2106 local_offer->AddGroup(bundle_group);
2107 remote_answer->AddGroup(bundle_group);
2108
2109 EXPECT_TRUE(transport_controller_
2110 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
2111 .ok());
2112 EXPECT_TRUE(transport_controller_
2113 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
2114 .ok());
2115 auto data_transport = transport_controller_->GetRtpTransport(kDataMid1);
2116
2117 // Add audio/video sections in subsequent offer.
2118 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
2119 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2120 nullptr);
2121 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
2122 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2123 nullptr);
2124 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
2125 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
2126 nullptr);
2127 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
2128 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
2129 nullptr);
2130
2131 // Reset the bundle group and do another offer/answer exchange.
2132 bundle_group.AddContentName(kAudioMid1);
2133 bundle_group.AddContentName(kVideoMid1);
2134 local_offer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
2135 remote_answer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
2136 local_offer->AddGroup(bundle_group);
2137 remote_answer->AddGroup(bundle_group);
2138
2139 EXPECT_TRUE(transport_controller_
2140 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
2141 .ok());
2142 EXPECT_TRUE(transport_controller_
2143 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
2144 .ok());
2145
2146 auto audio_transport = transport_controller_->GetRtpTransport(kAudioMid1);
2147 auto video_transport = transport_controller_->GetRtpTransport(kVideoMid1);
2148 EXPECT_EQ(data_transport, audio_transport);
2149 EXPECT_EQ(data_transport, video_transport);
2150 }
2151
TEST_F(JsepTransportControllerTest,VideoDataRejectedInAnswer)2152 TEST_F(JsepTransportControllerTest, VideoDataRejectedInAnswer) {
2153 CreateJsepTransportController(JsepTransportController::Config());
2154 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
2155 bundle_group.AddContentName(kAudioMid1);
2156 bundle_group.AddContentName(kVideoMid1);
2157 bundle_group.AddContentName(kDataMid1);
2158
2159 auto local_offer = std::make_unique<cricket::SessionDescription>();
2160 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
2161 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2162 nullptr);
2163 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
2164 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2165 nullptr);
2166 AddDataSection(local_offer.get(), kDataMid1,
2167 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
2168 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2169 nullptr);
2170
2171 auto remote_answer = std::make_unique<cricket::SessionDescription>();
2172 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
2173 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
2174 nullptr);
2175 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
2176 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
2177 nullptr);
2178 AddDataSection(remote_answer.get(), kDataMid1,
2179 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
2180 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
2181 nullptr);
2182 // Reject video and data section.
2183 remote_answer->contents()[1].rejected = true;
2184 remote_answer->contents()[2].rejected = true;
2185
2186 local_offer->AddGroup(bundle_group);
2187 remote_answer->AddGroup(bundle_group);
2188
2189 EXPECT_TRUE(transport_controller_
2190 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
2191 .ok());
2192 EXPECT_TRUE(transport_controller_
2193 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
2194 .ok());
2195
2196 // Verify the RtpTransport/DtlsTransport is destroyed correctly.
2197 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kVideoMid1));
2198 EXPECT_EQ(nullptr, transport_controller_->GetDtlsTransport(kDataMid1));
2199 // Verify the signals are fired correctly.
2200 auto it = changed_rtp_transport_by_mid_.find(kVideoMid1);
2201 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
2202 EXPECT_EQ(nullptr, it->second);
2203 auto it2 = changed_dtls_transport_by_mid_.find(kDataMid1);
2204 ASSERT_TRUE(it2 != changed_dtls_transport_by_mid_.end());
2205 EXPECT_EQ(nullptr, it2->second);
2206 }
2207
2208 // Tests that changing the bundled MID in subsequent offer/answer exchange is
2209 // not supported.
2210 // TODO(bugs.webrtc.org/6704): Change this test to expect success once issue is
2211 // fixed
TEST_F(JsepTransportControllerTest,ChangeBundledMidNotSupported)2212 TEST_F(JsepTransportControllerTest, ChangeBundledMidNotSupported) {
2213 CreateJsepTransportController(JsepTransportController::Config());
2214 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
2215 bundle_group.AddContentName(kAudioMid1);
2216 bundle_group.AddContentName(kVideoMid1);
2217
2218 auto local_offer = std::make_unique<cricket::SessionDescription>();
2219 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
2220 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2221 nullptr);
2222 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
2223 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2224 nullptr);
2225
2226 auto remote_answer = std::make_unique<cricket::SessionDescription>();
2227 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
2228 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
2229 nullptr);
2230 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
2231 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
2232 nullptr);
2233
2234 local_offer->AddGroup(bundle_group);
2235 remote_answer->AddGroup(bundle_group);
2236 EXPECT_TRUE(transport_controller_
2237 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
2238 .ok());
2239 EXPECT_TRUE(transport_controller_
2240 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
2241 .ok());
2242 EXPECT_EQ(transport_controller_->GetRtpTransport(kAudioMid1),
2243 transport_controller_->GetRtpTransport(kVideoMid1));
2244
2245 // Reorder the bundle group.
2246 EXPECT_TRUE(bundle_group.RemoveContentName(kAudioMid1));
2247 bundle_group.AddContentName(kAudioMid1);
2248 // The answerer uses the new bundle group and now the bundle mid is changed to
2249 // `kVideo1`.
2250 remote_answer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
2251 remote_answer->AddGroup(bundle_group);
2252 EXPECT_TRUE(transport_controller_
2253 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
2254 .ok());
2255 EXPECT_FALSE(transport_controller_
2256 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
2257 .ok());
2258 }
2259 // Test that rejecting only the first m= section of a BUNDLE group is treated as
2260 // an error, but rejecting all of them works as expected.
TEST_F(JsepTransportControllerTest,RejectFirstContentInBundleGroup)2261 TEST_F(JsepTransportControllerTest, RejectFirstContentInBundleGroup) {
2262 CreateJsepTransportController(JsepTransportController::Config());
2263 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
2264 bundle_group.AddContentName(kAudioMid1);
2265 bundle_group.AddContentName(kVideoMid1);
2266 bundle_group.AddContentName(kDataMid1);
2267
2268 auto local_offer = std::make_unique<cricket::SessionDescription>();
2269 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
2270 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2271 nullptr);
2272 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
2273 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2274 nullptr);
2275 AddDataSection(local_offer.get(), kDataMid1,
2276 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
2277 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2278 nullptr);
2279
2280 auto remote_answer = std::make_unique<cricket::SessionDescription>();
2281 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
2282 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
2283 nullptr);
2284 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
2285 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
2286 nullptr);
2287 AddDataSection(remote_answer.get(), kDataMid1,
2288 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
2289 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
2290 nullptr);
2291 // Reject audio content in answer.
2292 remote_answer->contents()[0].rejected = true;
2293
2294 local_offer->AddGroup(bundle_group);
2295 remote_answer->AddGroup(bundle_group);
2296
2297 EXPECT_TRUE(transport_controller_
2298 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
2299 .ok());
2300 EXPECT_FALSE(transport_controller_
2301 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
2302 .ok());
2303
2304 // Reject all the contents.
2305 remote_answer->contents()[1].rejected = true;
2306 remote_answer->contents()[2].rejected = true;
2307 EXPECT_TRUE(transport_controller_
2308 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
2309 .ok());
2310 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kAudioMid1));
2311 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kVideoMid1));
2312 EXPECT_EQ(nullptr, transport_controller_->GetDtlsTransport(kDataMid1));
2313 }
2314
2315 // Tests that applying non-RTCP-mux offer would fail when kRtcpMuxPolicyRequire
2316 // is used.
TEST_F(JsepTransportControllerTest,ApplyNonRtcpMuxOfferWhenMuxingRequired)2317 TEST_F(JsepTransportControllerTest, ApplyNonRtcpMuxOfferWhenMuxingRequired) {
2318 JsepTransportController::Config config;
2319 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
2320 CreateJsepTransportController(config);
2321 auto local_offer = std::make_unique<cricket::SessionDescription>();
2322 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
2323 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2324 nullptr);
2325
2326 local_offer->contents()[0].media_description()->set_rtcp_mux(false);
2327 // Applying a non-RTCP-mux offer is expected to fail.
2328 EXPECT_FALSE(transport_controller_
2329 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
2330 .ok());
2331 }
2332
2333 // Tests that applying non-RTCP-mux answer would fail when kRtcpMuxPolicyRequire
2334 // is used.
TEST_F(JsepTransportControllerTest,ApplyNonRtcpMuxAnswerWhenMuxingRequired)2335 TEST_F(JsepTransportControllerTest, ApplyNonRtcpMuxAnswerWhenMuxingRequired) {
2336 JsepTransportController::Config config;
2337 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
2338 CreateJsepTransportController(config);
2339 auto local_offer = std::make_unique<cricket::SessionDescription>();
2340 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
2341 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2342 nullptr);
2343 EXPECT_TRUE(transport_controller_
2344 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
2345 .ok());
2346
2347 auto remote_answer = std::make_unique<cricket::SessionDescription>();
2348 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
2349 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
2350 nullptr);
2351 // Applying a non-RTCP-mux answer is expected to fail.
2352 remote_answer->contents()[0].media_description()->set_rtcp_mux(false);
2353 EXPECT_FALSE(transport_controller_
2354 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
2355 .ok());
2356 }
2357
2358 // This tests that the BUNDLE group in answer should be a subset of the offered
2359 // group.
TEST_F(JsepTransportControllerTest,AddContentToBundleGroupInAnswerNotSupported)2360 TEST_F(JsepTransportControllerTest,
2361 AddContentToBundleGroupInAnswerNotSupported) {
2362 CreateJsepTransportController(JsepTransportController::Config());
2363 auto local_offer = CreateSessionDescriptionWithoutBundle();
2364 auto remote_answer = CreateSessionDescriptionWithoutBundle();
2365
2366 cricket::ContentGroup offer_bundle_group(cricket::GROUP_TYPE_BUNDLE);
2367 offer_bundle_group.AddContentName(kAudioMid1);
2368 local_offer->AddGroup(offer_bundle_group);
2369
2370 cricket::ContentGroup answer_bundle_group(cricket::GROUP_TYPE_BUNDLE);
2371 answer_bundle_group.AddContentName(kAudioMid1);
2372 answer_bundle_group.AddContentName(kVideoMid1);
2373 remote_answer->AddGroup(answer_bundle_group);
2374 EXPECT_TRUE(transport_controller_
2375 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
2376 .ok());
2377 EXPECT_FALSE(transport_controller_
2378 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
2379 .ok());
2380 }
2381
2382 // This tests that the BUNDLE group with non-existing MID should be rejectd.
TEST_F(JsepTransportControllerTest,RejectBundleGroupWithNonExistingMid)2383 TEST_F(JsepTransportControllerTest, RejectBundleGroupWithNonExistingMid) {
2384 CreateJsepTransportController(JsepTransportController::Config());
2385 auto local_offer = CreateSessionDescriptionWithoutBundle();
2386 auto remote_answer = CreateSessionDescriptionWithoutBundle();
2387
2388 cricket::ContentGroup invalid_bundle_group(cricket::GROUP_TYPE_BUNDLE);
2389 // The BUNDLE group is invalid because there is no data section in the
2390 // description.
2391 invalid_bundle_group.AddContentName(kDataMid1);
2392 local_offer->AddGroup(invalid_bundle_group);
2393 remote_answer->AddGroup(invalid_bundle_group);
2394
2395 EXPECT_FALSE(transport_controller_
2396 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
2397 .ok());
2398 EXPECT_FALSE(transport_controller_
2399 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
2400 .ok());
2401 }
2402
2403 // This tests that an answer shouldn't be able to remove an m= section from an
2404 // established group without rejecting it.
TEST_F(JsepTransportControllerTest,RemoveContentFromBundleGroup)2405 TEST_F(JsepTransportControllerTest, RemoveContentFromBundleGroup) {
2406 CreateJsepTransportController(JsepTransportController::Config());
2407
2408 auto local_offer = CreateSessionDescriptionWithBundleGroup();
2409 auto remote_answer = CreateSessionDescriptionWithBundleGroup();
2410 EXPECT_TRUE(transport_controller_
2411 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
2412 .ok());
2413 EXPECT_TRUE(transport_controller_
2414 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
2415 .ok());
2416
2417 // Do an re-offer/answer.
2418 EXPECT_TRUE(transport_controller_
2419 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
2420 .ok());
2421 auto new_answer = CreateSessionDescriptionWithoutBundle();
2422 cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
2423 // The answer removes video from the BUNDLE group without rejecting it is
2424 // invalid.
2425 new_bundle_group.AddContentName(kAudioMid1);
2426 new_answer->AddGroup(new_bundle_group);
2427
2428 // Applying invalid answer is expected to fail.
2429 EXPECT_FALSE(transport_controller_
2430 ->SetRemoteDescription(SdpType::kAnswer, new_answer.get())
2431 .ok());
2432
2433 // Rejected the video content.
2434 auto video_content = new_answer->GetContentByName(kVideoMid1);
2435 ASSERT_TRUE(video_content);
2436 video_content->rejected = true;
2437 EXPECT_TRUE(transport_controller_
2438 ->SetRemoteDescription(SdpType::kAnswer, new_answer.get())
2439 .ok());
2440 }
2441
2442 // Test that the JsepTransportController can process a new local and remote
2443 // description that changes the tagged BUNDLE group with the max-bundle policy
2444 // specified.
2445 // This is a regression test for bugs.webrtc.org/9954
TEST_F(JsepTransportControllerTest,ChangeTaggedMediaSectionMaxBundle)2446 TEST_F(JsepTransportControllerTest, ChangeTaggedMediaSectionMaxBundle) {
2447 CreateJsepTransportController(JsepTransportController::Config());
2448
2449 auto local_offer = std::make_unique<cricket::SessionDescription>();
2450 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
2451 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2452 nullptr);
2453 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
2454 bundle_group.AddContentName(kAudioMid1);
2455 local_offer->AddGroup(bundle_group);
2456 EXPECT_TRUE(transport_controller_
2457 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
2458 .ok());
2459
2460 std::unique_ptr<cricket::SessionDescription> remote_answer(
2461 local_offer->Clone());
2462 EXPECT_TRUE(transport_controller_
2463 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
2464 .ok());
2465
2466 std::unique_ptr<cricket::SessionDescription> local_reoffer(
2467 local_offer->Clone());
2468 local_reoffer->contents()[0].rejected = true;
2469 AddVideoSection(local_reoffer.get(), kVideoMid1, kIceUfrag1, kIcePwd1,
2470 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2471 nullptr);
2472 local_reoffer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
2473 cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
2474 new_bundle_group.AddContentName(kVideoMid1);
2475 local_reoffer->AddGroup(new_bundle_group);
2476
2477 EXPECT_TRUE(transport_controller_
2478 ->SetLocalDescription(SdpType::kOffer, local_reoffer.get())
2479 .ok());
2480 std::unique_ptr<cricket::SessionDescription> remote_reanswer(
2481 local_reoffer->Clone());
2482 EXPECT_TRUE(
2483 transport_controller_
2484 ->SetRemoteDescription(SdpType::kAnswer, remote_reanswer.get())
2485 .ok());
2486 }
2487
TEST_F(JsepTransportControllerTest,RollbackRestoresRejectedTransport)2488 TEST_F(JsepTransportControllerTest, RollbackRestoresRejectedTransport) {
2489 static const char kMid1Audio[] = "1_audio";
2490
2491 // Perform initial offer/answer.
2492 CreateJsepTransportController(JsepTransportController::Config());
2493 auto local_offer = std::make_unique<cricket::SessionDescription>();
2494 AddAudioSection(local_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
2495 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2496 nullptr);
2497 std::unique_ptr<cricket::SessionDescription> remote_answer(
2498 local_offer->Clone());
2499 EXPECT_TRUE(transport_controller_
2500 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
2501 .ok());
2502 EXPECT_TRUE(transport_controller_
2503 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
2504 .ok());
2505
2506 auto mid1_transport = transport_controller_->GetRtpTransport(kMid1Audio);
2507
2508 // Apply a reoffer which rejects the m= section, causing the transport to be
2509 // set to null.
2510 auto local_reoffer = std::make_unique<cricket::SessionDescription>();
2511 AddAudioSection(local_reoffer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
2512 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2513 nullptr);
2514 local_reoffer->contents()[0].rejected = true;
2515
2516 EXPECT_TRUE(transport_controller_
2517 ->SetLocalDescription(SdpType::kOffer, local_reoffer.get())
2518 .ok());
2519 auto old_mid1_transport = mid1_transport;
2520 mid1_transport = transport_controller_->GetRtpTransport(kMid1Audio);
2521 EXPECT_EQ(nullptr, mid1_transport);
2522
2523 // Rolling back shouldn't just create a new transport for MID 1, it should
2524 // restore the old transport.
2525 EXPECT_TRUE(transport_controller_->RollbackTransports().ok());
2526 mid1_transport = transport_controller_->GetRtpTransport(kMid1Audio);
2527 EXPECT_EQ(old_mid1_transport, mid1_transport);
2528 }
2529
2530 // If an offer with a modified BUNDLE group causes a MID->transport mapping to
2531 // change, rollback should restore the previous mapping.
TEST_F(JsepTransportControllerTest,RollbackRestoresPreviousTransportMapping)2532 TEST_F(JsepTransportControllerTest, RollbackRestoresPreviousTransportMapping) {
2533 static const char kMid1Audio[] = "1_audio";
2534 static const char kMid2Audio[] = "2_audio";
2535 static const char kMid3Audio[] = "3_audio";
2536
2537 // Perform an initial offer/answer to establish a (kMid1Audio,kMid2Audio)
2538 // group.
2539 CreateJsepTransportController(JsepTransportController::Config());
2540 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
2541 bundle_group.AddContentName(kMid1Audio);
2542 bundle_group.AddContentName(kMid2Audio);
2543
2544 auto local_offer = std::make_unique<cricket::SessionDescription>();
2545 AddAudioSection(local_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
2546 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2547 nullptr);
2548 AddVideoSection(local_offer.get(), kMid2Audio, kIceUfrag2, kIcePwd2,
2549 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2550 nullptr);
2551 AddAudioSection(local_offer.get(), kMid3Audio, kIceUfrag3, kIcePwd3,
2552 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2553 nullptr);
2554 local_offer->AddGroup(bundle_group);
2555
2556 std::unique_ptr<cricket::SessionDescription> remote_answer(
2557 local_offer->Clone());
2558
2559 EXPECT_TRUE(transport_controller_
2560 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
2561 .ok());
2562 EXPECT_TRUE(transport_controller_
2563 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
2564 .ok());
2565
2566 auto mid1_transport = transport_controller_->GetRtpTransport(kMid1Audio);
2567 auto mid2_transport = transport_controller_->GetRtpTransport(kMid2Audio);
2568 auto mid3_transport = transport_controller_->GetRtpTransport(kMid3Audio);
2569 EXPECT_EQ(mid1_transport, mid2_transport);
2570 EXPECT_NE(mid1_transport, mid3_transport);
2571
2572 // Apply a reoffer adding kMid3Audio to the group; transport mapping should
2573 // change, even without an answer, since this is an existing group.
2574 bundle_group.AddContentName(kMid3Audio);
2575 auto local_reoffer = std::make_unique<cricket::SessionDescription>();
2576 AddAudioSection(local_reoffer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
2577 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2578 nullptr);
2579 AddVideoSection(local_reoffer.get(), kMid2Audio, kIceUfrag2, kIcePwd2,
2580 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2581 nullptr);
2582 AddAudioSection(local_reoffer.get(), kMid3Audio, kIceUfrag3, kIcePwd3,
2583 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2584 nullptr);
2585 local_reoffer->AddGroup(bundle_group);
2586
2587 EXPECT_TRUE(transport_controller_
2588 ->SetLocalDescription(SdpType::kOffer, local_reoffer.get())
2589 .ok());
2590
2591 // Store the old transport pointer and verify that the offer actually changed
2592 // transports.
2593 auto old_mid3_transport = mid3_transport;
2594 mid1_transport = transport_controller_->GetRtpTransport(kMid1Audio);
2595 mid2_transport = transport_controller_->GetRtpTransport(kMid2Audio);
2596 mid3_transport = transport_controller_->GetRtpTransport(kMid3Audio);
2597 EXPECT_EQ(mid1_transport, mid2_transport);
2598 EXPECT_EQ(mid1_transport, mid3_transport);
2599
2600 // Rolling back shouldn't just create a new transport for MID 3, it should
2601 // restore the old transport.
2602 EXPECT_TRUE(transport_controller_->RollbackTransports().ok());
2603 mid3_transport = transport_controller_->GetRtpTransport(kMid3Audio);
2604 EXPECT_EQ(old_mid3_transport, mid3_transport);
2605 }
2606
2607 // Test that if an offer adds a MID to a specific BUNDLE group and is then
2608 // rolled back, it can be added to a different BUNDLE group in a new offer.
2609 // This is effectively testing that rollback resets the BundleManager state.
TEST_F(JsepTransportControllerTest,RollbackAndAddToDifferentBundleGroup)2610 TEST_F(JsepTransportControllerTest, RollbackAndAddToDifferentBundleGroup) {
2611 static const char kMid1Audio[] = "1_audio";
2612 static const char kMid2Audio[] = "2_audio";
2613 static const char kMid3Audio[] = "3_audio";
2614
2615 // Perform an initial offer/answer to establish two bundle groups, each with
2616 // one MID.
2617 CreateJsepTransportController(JsepTransportController::Config());
2618 cricket::ContentGroup bundle_group1(cricket::GROUP_TYPE_BUNDLE);
2619 bundle_group1.AddContentName(kMid1Audio);
2620 cricket::ContentGroup bundle_group2(cricket::GROUP_TYPE_BUNDLE);
2621 bundle_group2.AddContentName(kMid2Audio);
2622
2623 auto local_offer = std::make_unique<cricket::SessionDescription>();
2624 AddAudioSection(local_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
2625 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2626 nullptr);
2627 AddVideoSection(local_offer.get(), kMid2Audio, kIceUfrag2, kIcePwd2,
2628 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2629 nullptr);
2630 local_offer->AddGroup(bundle_group1);
2631 local_offer->AddGroup(bundle_group2);
2632
2633 std::unique_ptr<cricket::SessionDescription> remote_answer(
2634 local_offer->Clone());
2635
2636 EXPECT_TRUE(transport_controller_
2637 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
2638 .ok());
2639 EXPECT_TRUE(transport_controller_
2640 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
2641 .ok());
2642
2643 // Apply an offer that adds kMid3Audio to the first BUNDLE group.,
2644 cricket::ContentGroup modified_bundle_group1(cricket::GROUP_TYPE_BUNDLE);
2645 modified_bundle_group1.AddContentName(kMid1Audio);
2646 modified_bundle_group1.AddContentName(kMid3Audio);
2647 auto subsequent_offer_1 = std::make_unique<cricket::SessionDescription>();
2648 AddAudioSection(subsequent_offer_1.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
2649 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2650 nullptr);
2651 AddVideoSection(subsequent_offer_1.get(), kMid2Audio, kIceUfrag2, kIcePwd2,
2652 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2653 nullptr);
2654 AddVideoSection(subsequent_offer_1.get(), kMid3Audio, kIceUfrag3, kIcePwd3,
2655 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2656 nullptr);
2657 subsequent_offer_1->AddGroup(modified_bundle_group1);
2658 subsequent_offer_1->AddGroup(bundle_group2);
2659
2660 EXPECT_TRUE(
2661 transport_controller_
2662 ->SetLocalDescription(SdpType::kOffer, subsequent_offer_1.get())
2663 .ok());
2664
2665 auto mid1_transport = transport_controller_->GetRtpTransport(kMid1Audio);
2666 auto mid2_transport = transport_controller_->GetRtpTransport(kMid2Audio);
2667 auto mid3_transport = transport_controller_->GetRtpTransport(kMid3Audio);
2668 EXPECT_NE(mid1_transport, mid2_transport);
2669 EXPECT_EQ(mid1_transport, mid3_transport);
2670
2671 // Rollback and expect the transport to be reset.
2672 EXPECT_TRUE(transport_controller_->RollbackTransports().ok());
2673 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kMid3Audio));
2674
2675 // Apply an offer that adds kMid3Audio to the second BUNDLE group.,
2676 cricket::ContentGroup modified_bundle_group2(cricket::GROUP_TYPE_BUNDLE);
2677 modified_bundle_group2.AddContentName(kMid2Audio);
2678 modified_bundle_group2.AddContentName(kMid3Audio);
2679 auto subsequent_offer_2 = std::make_unique<cricket::SessionDescription>();
2680 AddAudioSection(subsequent_offer_2.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
2681 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2682 nullptr);
2683 AddVideoSection(subsequent_offer_2.get(), kMid2Audio, kIceUfrag2, kIcePwd2,
2684 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2685 nullptr);
2686 AddVideoSection(subsequent_offer_2.get(), kMid3Audio, kIceUfrag3, kIcePwd3,
2687 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
2688 nullptr);
2689 subsequent_offer_2->AddGroup(bundle_group1);
2690 subsequent_offer_2->AddGroup(modified_bundle_group2);
2691
2692 EXPECT_TRUE(
2693 transport_controller_
2694 ->SetLocalDescription(SdpType::kOffer, subsequent_offer_2.get())
2695 .ok());
2696
2697 mid1_transport = transport_controller_->GetRtpTransport(kMid1Audio);
2698 mid2_transport = transport_controller_->GetRtpTransport(kMid2Audio);
2699 mid3_transport = transport_controller_->GetRtpTransport(kMid3Audio);
2700 EXPECT_NE(mid1_transport, mid2_transport);
2701 EXPECT_EQ(mid2_transport, mid3_transport);
2702 }
2703
2704 } // namespace webrtc
2705