xref: /aosp_15_r20/external/webrtc/pc/jsep_transport_controller_unittest.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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