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