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