blob: 7bdba23c2d80a83e7de397bbdac9012d76ebe484 [file] [log] [blame]
Zhi Huange818b6e2018-02-22 23:26:271/*
2 * Copyright 2018 The WebRTC Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
Jonas Olssona4d87372019-07-05 17:08:3311#include "pc/jsep_transport_controller.h"
12
Zhi Huange818b6e2018-02-22 23:26:2713#include <map>
14#include <memory>
15
Qingsi Wang25ec8882019-11-15 20:33:0516#include "p2p/base/dtls_transport_factory.h"
Steve Anton10542f22019-01-11 17:11:0017#include "p2p/base/fake_dtls_transport.h"
18#include "p2p/base/fake_ice_transport.h"
Steve Anton10542f22019-01-11 17:11:0019#include "p2p/base/transport_info.h"
Zhi Huange818b6e2018-02-22 23:26:2720#include "rtc_base/gunit.h"
Zhi Huange818b6e2018-02-22 23:26:2721#include "rtc_base/thread.h"
22#include "test/gtest.h"
23
Zhi Huange818b6e2018-02-22 23:26:2724using cricket::Candidate;
25using cricket::Candidates;
Jonas Olssona4d87372019-07-05 17:08:3326using cricket::FakeDtlsTransport;
Zhi Huange818b6e2018-02-22 23:26:2727using webrtc::SdpType;
28
29static const int kTimeout = 100;
30static const char kIceUfrag1[] = "u0001";
31static const char kIcePwd1[] = "TESTICEPWD00000000000001";
32static const char kIceUfrag2[] = "u0002";
33static const char kIcePwd2[] = "TESTICEPWD00000000000002";
34static const char kIceUfrag3[] = "u0003";
35static const char kIcePwd3[] = "TESTICEPWD00000000000003";
36static const char kAudioMid1[] = "audio1";
37static const char kAudioMid2[] = "audio2";
38static const char kVideoMid1[] = "video1";
39static const char kVideoMid2[] = "video2";
40static const char kDataMid1[] = "data1";
41
42namespace webrtc {
43
Qingsi Wang25ec8882019-11-15 20:33:0544class FakeIceTransportFactory : public webrtc::IceTransportFactory {
Zhi Huange818b6e2018-02-22 23:26:2745 public:
Qingsi Wang25ec8882019-11-15 20:33:0546 ~FakeIceTransportFactory() override = default;
47 rtc::scoped_refptr<IceTransportInterface> CreateIceTransport(
Zhi Huange818b6e2018-02-22 23:26:2748 const std::string& transport_name,
Qingsi Wang25ec8882019-11-15 20:33:0549 int component,
50 IceTransportInit init) override {
51 return new rtc::RefCountedObject<cricket::FakeIceTransportWrapper>(
52 std::make_unique<cricket::FakeIceTransport>(transport_name, component));
Zhi Huange818b6e2018-02-22 23:26:2753 }
Qingsi Wang25ec8882019-11-15 20:33:0554};
Zhi Huange818b6e2018-02-22 23:26:2755
Qingsi Wang25ec8882019-11-15 20:33:0556class FakeDtlsTransportFactory : public cricket::DtlsTransportFactory {
57 public:
Zhi Huange818b6e2018-02-22 23:26:2758 std::unique_ptr<cricket::DtlsTransportInternal> CreateDtlsTransport(
Bjorn A Mellem0c1c1b42019-05-30 00:34:1359 cricket::IceTransportInternal* ice,
Benjamin Wrighta54daf12018-10-11 22:33:1760 const webrtc::CryptoOptions& crypto_options) override {
Mirko Bonadei317a1f02019-09-17 15:06:1861 return std::make_unique<FakeDtlsTransport>(
Bjorn A Mellem0c1c1b42019-05-30 00:34:1362 static_cast<cricket::FakeIceTransport*>(ice));
Zhi Huange818b6e2018-02-22 23:26:2763 }
64};
65
Zhi Huang365381f2018-04-13 23:44:3466class JsepTransportControllerTest : public JsepTransportController::Observer,
Mirko Bonadei6a489f22019-04-09 13:11:1267 public ::testing::Test,
Zhi Huange818b6e2018-02-22 23:26:2768 public sigslot::has_slots<> {
69 public:
70 JsepTransportControllerTest() : signaling_thread_(rtc::Thread::Current()) {
Qingsi Wang25ec8882019-11-15 20:33:0571 fake_ice_transport_factory_ = std::make_unique<FakeIceTransportFactory>();
72 fake_dtls_transport_factory_ = std::make_unique<FakeDtlsTransportFactory>();
Zhi Huange818b6e2018-02-22 23:26:2773 }
74
75 void CreateJsepTransportController(
76 JsepTransportController::Config config,
77 rtc::Thread* signaling_thread = rtc::Thread::Current(),
78 rtc::Thread* network_thread = rtc::Thread::Current(),
79 cricket::PortAllocator* port_allocator = nullptr) {
Zhi Huang365381f2018-04-13 23:44:3480 config.transport_observer = this;
Sebastian Jansson1b83a9e2019-09-18 16:22:1281 config.rtcp_handler = [](const rtc::CopyOnWriteBuffer& packet,
82 int64_t packet_time_us) { RTC_NOTREACHED(); };
Qingsi Wang25ec8882019-11-15 20:33:0583 config.ice_transport_factory = fake_ice_transport_factory_.get();
84 config.dtls_transport_factory = fake_dtls_transport_factory_.get();
Mirko Bonadei317a1f02019-09-17 15:06:1885 transport_controller_ = std::make_unique<JsepTransportController>(
Qingsi Wange8d54b92020-01-06 22:31:5786 signaling_thread, network_thread, port_allocator,
87 nullptr /* async_resolver_factory */, config);
Zhi Huange818b6e2018-02-22 23:26:2788 ConnectTransportControllerSignals();
89 }
90
91 void ConnectTransportControllerSignals() {
92 transport_controller_->SignalIceConnectionState.connect(
93 this, &JsepTransportControllerTest::OnConnectionState);
Alex Loiko9289eda2018-11-23 16:18:5994 transport_controller_->SignalStandardizedIceConnectionState.connect(
95 this, &JsepTransportControllerTest::OnStandardizedIceConnectionState);
Jonas Olsson635474e2018-10-18 13:58:1796 transport_controller_->SignalConnectionState.connect(
97 this, &JsepTransportControllerTest::OnCombinedConnectionState);
Zhi Huange818b6e2018-02-22 23:26:2798 transport_controller_->SignalIceGatheringState.connect(
99 this, &JsepTransportControllerTest::OnGatheringState);
100 transport_controller_->SignalIceCandidatesGathered.connect(
101 this, &JsepTransportControllerTest::OnCandidatesGathered);
Zhi Huange818b6e2018-02-22 23:26:27102 }
103
104 std::unique_ptr<cricket::SessionDescription>
105 CreateSessionDescriptionWithoutBundle() {
Mirko Bonadei317a1f02019-09-17 15:06:18106 auto description = std::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 23:26:27107 AddAudioSection(description.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
108 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
109 nullptr);
110 AddVideoSection(description.get(), kVideoMid1, kIceUfrag1, kIcePwd1,
111 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
112 nullptr);
113 return description;
114 }
115
116 std::unique_ptr<cricket::SessionDescription>
117 CreateSessionDescriptionWithBundleGroup() {
118 auto description = CreateSessionDescriptionWithoutBundle();
119 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
120 bundle_group.AddContentName(kAudioMid1);
121 bundle_group.AddContentName(kVideoMid1);
122 description->AddGroup(bundle_group);
123
124 return description;
125 }
126
Bjorn A Mellem8e1343a2019-09-30 22:12:47127 std::unique_ptr<cricket::SessionDescription>
128 CreateSessionDescriptionWithBundledData() {
129 auto description = CreateSessionDescriptionWithoutBundle();
130 AddDataSection(description.get(), kDataMid1,
131 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
132 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
133 nullptr);
134 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
135 bundle_group.AddContentName(kAudioMid1);
136 bundle_group.AddContentName(kVideoMid1);
137 bundle_group.AddContentName(kDataMid1);
138 description->AddGroup(bundle_group);
139 return description;
140 }
141
Zhi Huange818b6e2018-02-22 23:26:27142 void AddAudioSection(cricket::SessionDescription* description,
143 const std::string& mid,
144 const std::string& ufrag,
145 const std::string& pwd,
146 cricket::IceMode ice_mode,
147 cricket::ConnectionRole conn_role,
148 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
149 std::unique_ptr<cricket::AudioContentDescription> audio(
150 new cricket::AudioContentDescription());
Zhi Huange830e682018-03-30 17:48:35151 // Set RTCP-mux to be true because the default policy is "mux required".
152 audio->set_rtcp_mux(true);
Zhi Huange818b6e2018-02-22 23:26:27153 description->AddContent(mid, cricket::MediaProtocolType::kRtp,
Harald Alvestrand1716d392019-06-03 18:35:45154 /*rejected=*/false, std::move(audio));
Zhi Huange818b6e2018-02-22 23:26:27155 AddTransportInfo(description, mid, ufrag, pwd, ice_mode, conn_role, cert);
156 }
157
158 void AddVideoSection(cricket::SessionDescription* description,
159 const std::string& mid,
160 const std::string& ufrag,
161 const std::string& pwd,
162 cricket::IceMode ice_mode,
163 cricket::ConnectionRole conn_role,
164 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
165 std::unique_ptr<cricket::VideoContentDescription> video(
166 new cricket::VideoContentDescription());
Zhi Huange830e682018-03-30 17:48:35167 // Set RTCP-mux to be true because the default policy is "mux required".
168 video->set_rtcp_mux(true);
Zhi Huange818b6e2018-02-22 23:26:27169 description->AddContent(mid, cricket::MediaProtocolType::kRtp,
Harald Alvestrand1716d392019-06-03 18:35:45170 /*rejected=*/false, std::move(video));
Zhi Huange818b6e2018-02-22 23:26:27171 AddTransportInfo(description, mid, ufrag, pwd, ice_mode, conn_role, cert);
172 }
173
174 void AddDataSection(cricket::SessionDescription* description,
175 const std::string& mid,
176 cricket::MediaProtocolType protocol_type,
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) {
Harald Alvestrand5fc28b12019-05-13 11:36:16182 RTC_CHECK(protocol_type == cricket::MediaProtocolType::kSctp);
183 std::unique_ptr<cricket::SctpDataContentDescription> data(
184 new cricket::SctpDataContentDescription());
Zhi Huange830e682018-03-30 17:48:35185 data->set_rtcp_mux(true);
Zhi Huange818b6e2018-02-22 23:26:27186 description->AddContent(mid, protocol_type,
Harald Alvestrand1716d392019-06-03 18:35:45187 /*rejected=*/false, std::move(data));
Zhi Huange818b6e2018-02-22 23:26:27188 AddTransportInfo(description, mid, ufrag, pwd, ice_mode, conn_role, cert);
189 }
190
191 void AddTransportInfo(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<rtc::SSLFingerprint> fingerprint;
199 if (cert) {
Steve Anton4905edb2018-10-16 02:27:44200 fingerprint = rtc::SSLFingerprint::CreateFromCertificate(*cert);
Zhi Huange818b6e2018-02-22 23:26:27201 }
202
203 cricket::TransportDescription transport_desc(std::vector<std::string>(),
204 ufrag, pwd, ice_mode,
205 conn_role, fingerprint.get());
206 description->AddTransportInfo(cricket::TransportInfo(mid, transport_desc));
207 }
208
209 cricket::IceConfig CreateIceConfig(
210 int receiving_timeout,
211 cricket::ContinualGatheringPolicy continual_gathering_policy) {
212 cricket::IceConfig config;
213 config.receiving_timeout = receiving_timeout;
214 config.continual_gathering_policy = continual_gathering_policy;
215 return config;
216 }
217
218 Candidate CreateCandidate(const std::string& transport_name, int component) {
219 Candidate c;
220 c.set_transport_name(transport_name);
221 c.set_address(rtc::SocketAddress("192.168.1.1", 8000));
222 c.set_component(component);
223 c.set_protocol(cricket::UDP_PROTOCOL_NAME);
224 c.set_priority(1);
225 return c;
226 }
227
228 void CreateLocalDescriptionAndCompleteConnectionOnNetworkThread() {
229 if (!network_thread_->IsCurrent()) {
230 network_thread_->Invoke<void>(RTC_FROM_HERE, [&] {
231 CreateLocalDescriptionAndCompleteConnectionOnNetworkThread();
232 });
233 return;
234 }
235
236 auto description = CreateSessionDescriptionWithBundleGroup();
237 EXPECT_TRUE(transport_controller_
238 ->SetLocalDescription(SdpType::kOffer, description.get())
239 .ok());
240
241 transport_controller_->MaybeStartGathering();
242 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
243 transport_controller_->GetDtlsTransport(kAudioMid1));
244 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
245 transport_controller_->GetDtlsTransport(kVideoMid1));
246 fake_audio_dtls->fake_ice_transport()->SignalCandidateGathered(
247 fake_audio_dtls->fake_ice_transport(),
248 CreateCandidate(kAudioMid1, /*component=*/1));
249 fake_video_dtls->fake_ice_transport()->SignalCandidateGathered(
250 fake_video_dtls->fake_ice_transport(),
251 CreateCandidate(kVideoMid1, /*component=*/1));
252 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
253 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
254 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(2);
255 fake_video_dtls->fake_ice_transport()->SetConnectionCount(2);
256 fake_audio_dtls->SetReceiving(true);
257 fake_video_dtls->SetReceiving(true);
258 fake_audio_dtls->SetWritable(true);
259 fake_video_dtls->SetWritable(true);
260 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
261 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
262 }
263
264 protected:
Alex Loiko9289eda2018-11-23 16:18:59265 void OnConnectionState(cricket::IceConnectionState state) {
Zhi Huange818b6e2018-02-22 23:26:27266 if (!signaling_thread_->IsCurrent()) {
267 signaled_on_non_signaling_thread_ = true;
268 }
269 connection_state_ = state;
270 ++connection_state_signal_count_;
271 }
272
Alex Loiko9289eda2018-11-23 16:18:59273 void OnStandardizedIceConnectionState(
274 PeerConnectionInterface::IceConnectionState state) {
275 if (!signaling_thread_->IsCurrent()) {
276 signaled_on_non_signaling_thread_ = true;
277 }
278 ice_connection_state_ = state;
279 ++ice_connection_state_signal_count_;
280 }
281
Jonas Olsson635474e2018-10-18 13:58:17282 void OnCombinedConnectionState(
283 PeerConnectionInterface::PeerConnectionState state) {
Jonas Olsson6a8727b2018-12-07 12:11:44284 RTC_LOG(LS_INFO) << "OnCombinedConnectionState: "
285 << static_cast<int>(state);
Jonas Olsson635474e2018-10-18 13:58:17286 if (!signaling_thread_->IsCurrent()) {
287 signaled_on_non_signaling_thread_ = true;
288 }
289 combined_connection_state_ = state;
290 ++combined_connection_state_signal_count_;
291 }
292
Zhi Huange818b6e2018-02-22 23:26:27293 void OnGatheringState(cricket::IceGatheringState state) {
294 if (!signaling_thread_->IsCurrent()) {
295 signaled_on_non_signaling_thread_ = true;
296 }
297 gathering_state_ = state;
298 ++gathering_state_signal_count_;
299 }
300
301 void OnCandidatesGathered(const std::string& transport_name,
302 const Candidates& candidates) {
303 if (!signaling_thread_->IsCurrent()) {
304 signaled_on_non_signaling_thread_ = true;
305 }
306 candidates_[transport_name].insert(candidates_[transport_name].end(),
307 candidates.begin(), candidates.end());
308 ++candidates_signal_count_;
309 }
310
Zhi Huang365381f2018-04-13 23:44:34311 // JsepTransportController::Observer overrides.
Bjorn A Mellemb689af42019-08-21 17:44:59312 bool OnTransportChanged(
313 const std::string& mid,
314 RtpTransportInternal* rtp_transport,
315 rtc::scoped_refptr<DtlsTransport> dtls_transport,
Bjorn A Mellembc3eebc2019-09-23 21:53:54316 DataChannelTransportInterface* data_channel_transport) override {
Taylor Brandstettercbaa2542018-04-16 23:42:14317 changed_rtp_transport_by_mid_[mid] = rtp_transport;
Harald Alvestrandc85328f2019-02-28 06:51:00318 if (dtls_transport) {
319 changed_dtls_transport_by_mid_[mid] = dtls_transport->internal();
320 } else {
321 changed_dtls_transport_by_mid_[mid] = nullptr;
322 }
Taylor Brandstettercbaa2542018-04-16 23:42:14323 return true;
Zhi Huange818b6e2018-02-22 23:26:27324 }
325
326 // Information received from signals from transport controller.
Alex Loiko9289eda2018-11-23 16:18:59327 cricket::IceConnectionState connection_state_ =
328 cricket::kIceConnectionConnecting;
329 PeerConnectionInterface::IceConnectionState ice_connection_state_ =
Jonas Olsson635474e2018-10-18 13:58:17330 PeerConnectionInterface::kIceConnectionNew;
331 PeerConnectionInterface::PeerConnectionState combined_connection_state_ =
332 PeerConnectionInterface::PeerConnectionState::kNew;
Zhi Huange818b6e2018-02-22 23:26:27333 bool receiving_ = false;
334 cricket::IceGatheringState gathering_state_ = cricket::kIceGatheringNew;
335 // transport_name => candidates
336 std::map<std::string, Candidates> candidates_;
337 // Counts of each signal emitted.
338 int connection_state_signal_count_ = 0;
Alex Loiko9289eda2018-11-23 16:18:59339 int ice_connection_state_signal_count_ = 0;
Jonas Olsson635474e2018-10-18 13:58:17340 int combined_connection_state_signal_count_ = 0;
Zhi Huange818b6e2018-02-22 23:26:27341 int receiving_signal_count_ = 0;
342 int gathering_state_signal_count_ = 0;
343 int candidates_signal_count_ = 0;
344
345 // |network_thread_| should be destroyed after |transport_controller_|
346 std::unique_ptr<rtc::Thread> network_thread_;
Qingsi Wang25ec8882019-11-15 20:33:05347 std::unique_ptr<FakeIceTransportFactory> fake_ice_transport_factory_;
348 std::unique_ptr<FakeDtlsTransportFactory> fake_dtls_transport_factory_;
Zhi Huange818b6e2018-02-22 23:26:27349 rtc::Thread* const signaling_thread_ = nullptr;
350 bool signaled_on_non_signaling_thread_ = false;
351 // Used to verify the SignalRtpTransportChanged/SignalDtlsTransportChanged are
352 // signaled correctly.
353 std::map<std::string, RtpTransportInternal*> changed_rtp_transport_by_mid_;
354 std::map<std::string, cricket::DtlsTransportInternal*>
355 changed_dtls_transport_by_mid_;
Piotr (Peter) Slatalacc8e8bb2018-11-15 16:26:19356
357 // Transport controller needs to be destroyed first, because it may issue
358 // callbacks that modify the changed_*_by_mid in the destructor.
359 std::unique_ptr<JsepTransportController> transport_controller_;
Zhi Huange818b6e2018-02-22 23:26:27360};
361
362TEST_F(JsepTransportControllerTest, GetRtpTransport) {
363 CreateJsepTransportController(JsepTransportController::Config());
364 auto description = CreateSessionDescriptionWithoutBundle();
365 EXPECT_TRUE(transport_controller_
366 ->SetLocalDescription(SdpType::kOffer, description.get())
367 .ok());
368 auto audio_rtp_transport = transport_controller_->GetRtpTransport(kAudioMid1);
369 auto video_rtp_transport = transport_controller_->GetRtpTransport(kVideoMid1);
370 EXPECT_NE(nullptr, audio_rtp_transport);
371 EXPECT_NE(nullptr, video_rtp_transport);
372 EXPECT_NE(audio_rtp_transport, video_rtp_transport);
373 // Return nullptr for non-existing ones.
374 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kAudioMid2));
375}
376
377TEST_F(JsepTransportControllerTest, GetDtlsTransport) {
378 JsepTransportController::Config config;
379 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
380 CreateJsepTransportController(config);
381 auto description = CreateSessionDescriptionWithoutBundle();
382 EXPECT_TRUE(transport_controller_
383 ->SetLocalDescription(SdpType::kOffer, description.get())
384 .ok());
385 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kAudioMid1));
386 EXPECT_NE(nullptr, transport_controller_->GetRtcpDtlsTransport(kAudioMid1));
Harald Alvestrandad88c882018-11-28 15:47:46387 EXPECT_NE(nullptr,
388 transport_controller_->LookupDtlsTransportByMid(kAudioMid1));
Zhi Huange818b6e2018-02-22 23:26:27389 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kVideoMid1));
390 EXPECT_NE(nullptr, transport_controller_->GetRtcpDtlsTransport(kVideoMid1));
Harald Alvestrandad88c882018-11-28 15:47:46391 EXPECT_NE(nullptr,
392 transport_controller_->LookupDtlsTransportByMid(kVideoMid1));
393 // Lookup for all MIDs should return different transports (no bundle)
394 EXPECT_NE(transport_controller_->LookupDtlsTransportByMid(kAudioMid1),
395 transport_controller_->LookupDtlsTransportByMid(kVideoMid1));
Zhi Huange818b6e2018-02-22 23:26:27396 // Return nullptr for non-existing ones.
397 EXPECT_EQ(nullptr, transport_controller_->GetDtlsTransport(kVideoMid2));
398 EXPECT_EQ(nullptr, transport_controller_->GetRtcpDtlsTransport(kVideoMid2));
Harald Alvestrandad88c882018-11-28 15:47:46399 EXPECT_EQ(nullptr,
400 transport_controller_->LookupDtlsTransportByMid(kVideoMid2));
Harald Alvestrand628f37a2018-12-06 09:55:20401 // Take a pointer to a transport, shut down the transport controller,
402 // and verify that the resulting container is empty.
403 auto dtls_transport =
404 transport_controller_->LookupDtlsTransportByMid(kVideoMid1);
405 webrtc::DtlsTransport* my_transport =
406 static_cast<DtlsTransport*>(dtls_transport.get());
407 EXPECT_NE(nullptr, my_transport->internal());
408 transport_controller_.reset();
409 EXPECT_EQ(nullptr, my_transport->internal());
Zhi Huange818b6e2018-02-22 23:26:27410}
411
Zhi Huange830e682018-03-30 17:48:35412TEST_F(JsepTransportControllerTest, GetDtlsTransportWithRtcpMux) {
413 JsepTransportController::Config config;
414 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
415 CreateJsepTransportController(config);
416 auto description = CreateSessionDescriptionWithoutBundle();
417 EXPECT_TRUE(transport_controller_
418 ->SetLocalDescription(SdpType::kOffer, description.get())
419 .ok());
420 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kAudioMid1));
421 EXPECT_EQ(nullptr, transport_controller_->GetRtcpDtlsTransport(kAudioMid1));
422 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kVideoMid1));
423 EXPECT_EQ(nullptr, transport_controller_->GetRtcpDtlsTransport(kVideoMid1));
Piotr (Peter) Slatala55b91b92019-01-25 21:31:15424}
425
Zhi Huange818b6e2018-02-22 23:26:27426TEST_F(JsepTransportControllerTest, SetIceConfig) {
427 CreateJsepTransportController(JsepTransportController::Config());
428 auto description = CreateSessionDescriptionWithoutBundle();
429 EXPECT_TRUE(transport_controller_
430 ->SetLocalDescription(SdpType::kOffer, description.get())
431 .ok());
432
433 transport_controller_->SetIceConfig(
434 CreateIceConfig(kTimeout, cricket::GATHER_CONTINUALLY));
435 FakeDtlsTransport* fake_audio_dtls = static_cast<FakeDtlsTransport*>(
436 transport_controller_->GetDtlsTransport(kAudioMid1));
437 ASSERT_NE(nullptr, fake_audio_dtls);
438 EXPECT_EQ(kTimeout,
439 fake_audio_dtls->fake_ice_transport()->receiving_timeout());
440 EXPECT_TRUE(fake_audio_dtls->fake_ice_transport()->gather_continually());
441
442 // Test that value stored in controller is applied to new transports.
443 AddAudioSection(description.get(), kAudioMid2, kIceUfrag1, kIcePwd1,
444 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
445 nullptr);
446
447 EXPECT_TRUE(transport_controller_
448 ->SetLocalDescription(SdpType::kOffer, description.get())
449 .ok());
450 fake_audio_dtls = static_cast<FakeDtlsTransport*>(
451 transport_controller_->GetDtlsTransport(kAudioMid2));
452 ASSERT_NE(nullptr, fake_audio_dtls);
453 EXPECT_EQ(kTimeout,
454 fake_audio_dtls->fake_ice_transport()->receiving_timeout());
455 EXPECT_TRUE(fake_audio_dtls->fake_ice_transport()->gather_continually());
456}
457
458// Tests the getter and setter of the ICE restart flag.
459TEST_F(JsepTransportControllerTest, NeedIceRestart) {
460 CreateJsepTransportController(JsepTransportController::Config());
461 auto description = CreateSessionDescriptionWithoutBundle();
462 EXPECT_TRUE(transport_controller_
463 ->SetLocalDescription(SdpType::kOffer, description.get())
464 .ok());
465 EXPECT_TRUE(transport_controller_
466 ->SetRemoteDescription(SdpType::kAnswer, description.get())
467 .ok());
468
469 // Initially NeedsIceRestart should return false.
470 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kAudioMid1));
471 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kVideoMid1));
472 // Set the needs-ice-restart flag and verify NeedsIceRestart starts returning
473 // true.
474 transport_controller_->SetNeedsIceRestartFlag();
475 EXPECT_TRUE(transport_controller_->NeedsIceRestart(kAudioMid1));
476 EXPECT_TRUE(transport_controller_->NeedsIceRestart(kVideoMid1));
477 // For a nonexistent transport, false should be returned.
478 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kVideoMid2));
479
480 // Reset the ice_ufrag/ice_pwd for audio.
481 auto audio_transport_info = description->GetTransportInfoByName(kAudioMid1);
482 audio_transport_info->description.ice_ufrag = kIceUfrag2;
483 audio_transport_info->description.ice_pwd = kIcePwd2;
484 EXPECT_TRUE(transport_controller_
485 ->SetLocalDescription(SdpType::kOffer, description.get())
486 .ok());
487 // Because the ICE is only restarted for audio, NeedsIceRestart is expected to
488 // return false for audio and true for video.
489 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kAudioMid1));
490 EXPECT_TRUE(transport_controller_->NeedsIceRestart(kVideoMid1));
491}
492
493TEST_F(JsepTransportControllerTest, MaybeStartGathering) {
494 CreateJsepTransportController(JsepTransportController::Config());
495 auto description = CreateSessionDescriptionWithBundleGroup();
496 EXPECT_TRUE(transport_controller_
497 ->SetLocalDescription(SdpType::kOffer, description.get())
498 .ok());
499 // After setting the local description, we should be able to start gathering
500 // candidates.
501 transport_controller_->MaybeStartGathering();
502 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
503 EXPECT_EQ(1, gathering_state_signal_count_);
504}
505
506TEST_F(JsepTransportControllerTest, AddRemoveRemoteCandidates) {
507 CreateJsepTransportController(JsepTransportController::Config());
508 auto description = CreateSessionDescriptionWithoutBundle();
509 transport_controller_->SetLocalDescription(SdpType::kOffer,
510 description.get());
511 transport_controller_->SetRemoteDescription(SdpType::kAnswer,
512 description.get());
513 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
514 transport_controller_->GetDtlsTransport(kAudioMid1));
515 ASSERT_NE(nullptr, fake_audio_dtls);
516 Candidates candidates;
517 candidates.push_back(
518 CreateCandidate(kAudioMid1, cricket::ICE_CANDIDATE_COMPONENT_RTP));
519 EXPECT_TRUE(
520 transport_controller_->AddRemoteCandidates(kAudioMid1, candidates).ok());
521 EXPECT_EQ(1U,
522 fake_audio_dtls->fake_ice_transport()->remote_candidates().size());
523
524 EXPECT_TRUE(transport_controller_->RemoveRemoteCandidates(candidates).ok());
525 EXPECT_EQ(0U,
526 fake_audio_dtls->fake_ice_transport()->remote_candidates().size());
527}
528
529TEST_F(JsepTransportControllerTest, SetAndGetLocalCertificate) {
530 CreateJsepTransportController(JsepTransportController::Config());
531
532 rtc::scoped_refptr<rtc::RTCCertificate> certificate1 =
Harald Alvestrand8515d5a2020-03-20 21:51:32533 rtc::RTCCertificate::Create(
534 rtc::SSLIdentity::Create("session1", rtc::KT_DEFAULT));
Zhi Huange818b6e2018-02-22 23:26:27535 rtc::scoped_refptr<rtc::RTCCertificate> returned_certificate;
536
Mirko Bonadei317a1f02019-09-17 15:06:18537 auto description = std::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 23:26:27538 AddAudioSection(description.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
539 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
540 certificate1);
541
542 // Apply the local certificate.
543 EXPECT_TRUE(transport_controller_->SetLocalCertificate(certificate1));
544 // Apply the local description.
545 EXPECT_TRUE(transport_controller_
546 ->SetLocalDescription(SdpType::kOffer, description.get())
547 .ok());
548 returned_certificate = transport_controller_->GetLocalCertificate(kAudioMid1);
549 EXPECT_TRUE(returned_certificate);
550 EXPECT_EQ(certificate1->identity()->certificate().ToPEMString(),
551 returned_certificate->identity()->certificate().ToPEMString());
552
553 // Should fail if called for a nonexistant transport.
554 EXPECT_EQ(nullptr, transport_controller_->GetLocalCertificate(kVideoMid1));
555
556 // Shouldn't be able to change the identity once set.
557 rtc::scoped_refptr<rtc::RTCCertificate> certificate2 =
Harald Alvestrand8515d5a2020-03-20 21:51:32558 rtc::RTCCertificate::Create(
559 rtc::SSLIdentity::Create("session2", rtc::KT_DEFAULT));
Zhi Huange818b6e2018-02-22 23:26:27560 EXPECT_FALSE(transport_controller_->SetLocalCertificate(certificate2));
561}
562
Taylor Brandstetterc3928662018-02-23 21:04:51563TEST_F(JsepTransportControllerTest, GetRemoteSSLCertChain) {
Zhi Huange818b6e2018-02-22 23:26:27564 CreateJsepTransportController(JsepTransportController::Config());
565 auto description = CreateSessionDescriptionWithBundleGroup();
566 EXPECT_TRUE(transport_controller_
567 ->SetLocalDescription(SdpType::kOffer, description.get())
568 .ok());
569 rtc::FakeSSLCertificate fake_certificate("fake_data");
570
571 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
572 transport_controller_->GetDtlsTransport(kAudioMid1));
573 fake_audio_dtls->SetRemoteSSLCertificate(&fake_certificate);
Taylor Brandstetterc3928662018-02-23 21:04:51574 std::unique_ptr<rtc::SSLCertChain> returned_cert_chain =
575 transport_controller_->GetRemoteSSLCertChain(kAudioMid1);
576 ASSERT_TRUE(returned_cert_chain);
577 ASSERT_EQ(1u, returned_cert_chain->GetSize());
Zhi Huange818b6e2018-02-22 23:26:27578 EXPECT_EQ(fake_certificate.ToPEMString(),
Taylor Brandstetterc3928662018-02-23 21:04:51579 returned_cert_chain->Get(0).ToPEMString());
Zhi Huange818b6e2018-02-22 23:26:27580
581 // Should fail if called for a nonexistant transport.
Taylor Brandstetterc3928662018-02-23 21:04:51582 EXPECT_FALSE(transport_controller_->GetRemoteSSLCertChain(kAudioMid2));
Zhi Huange818b6e2018-02-22 23:26:27583}
584
585TEST_F(JsepTransportControllerTest, GetDtlsRole) {
586 CreateJsepTransportController(JsepTransportController::Config());
Harald Alvestrand8515d5a2020-03-20 21:51:32587 auto offer_certificate = rtc::RTCCertificate::Create(
588 rtc::SSLIdentity::Create("offer", rtc::KT_DEFAULT));
589 auto answer_certificate = rtc::RTCCertificate::Create(
590 rtc::SSLIdentity::Create("answer", rtc::KT_DEFAULT));
Zhi Huange818b6e2018-02-22 23:26:27591 transport_controller_->SetLocalCertificate(offer_certificate);
592
Mirko Bonadei317a1f02019-09-17 15:06:18593 auto offer_desc = std::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 23:26:27594 AddAudioSection(offer_desc.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
595 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
596 offer_certificate);
Mirko Bonadei317a1f02019-09-17 15:06:18597 auto answer_desc = std::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 23:26:27598 AddAudioSection(answer_desc.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
599 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
600 answer_certificate);
601
602 EXPECT_TRUE(transport_controller_
603 ->SetLocalDescription(SdpType::kOffer, offer_desc.get())
604 .ok());
605
Danil Chapovalov66cadcc2018-06-19 14:47:43606 absl::optional<rtc::SSLRole> role =
Zhi Huange818b6e2018-02-22 23:26:27607 transport_controller_->GetDtlsRole(kAudioMid1);
608 // The DTLS role is not decided yet.
609 EXPECT_FALSE(role);
610 EXPECT_TRUE(transport_controller_
611 ->SetRemoteDescription(SdpType::kAnswer, answer_desc.get())
612 .ok());
613 role = transport_controller_->GetDtlsRole(kAudioMid1);
614
615 ASSERT_TRUE(role);
616 EXPECT_EQ(rtc::SSL_CLIENT, *role);
617}
618
619TEST_F(JsepTransportControllerTest, GetStats) {
620 CreateJsepTransportController(JsepTransportController::Config());
621 auto description = CreateSessionDescriptionWithBundleGroup();
622 EXPECT_TRUE(transport_controller_
623 ->SetLocalDescription(SdpType::kOffer, description.get())
624 .ok());
625
626 cricket::TransportStats stats;
627 EXPECT_TRUE(transport_controller_->GetStats(kAudioMid1, &stats));
628 EXPECT_EQ(kAudioMid1, stats.transport_name);
629 EXPECT_EQ(1u, stats.channel_stats.size());
630 // Return false for non-existing transport.
631 EXPECT_FALSE(transport_controller_->GetStats(kAudioMid2, &stats));
632}
633
634TEST_F(JsepTransportControllerTest, SignalConnectionStateFailed) {
635 CreateJsepTransportController(JsepTransportController::Config());
636 auto description = CreateSessionDescriptionWithoutBundle();
637 EXPECT_TRUE(transport_controller_
638 ->SetLocalDescription(SdpType::kOffer, description.get())
639 .ok());
640
641 auto fake_ice = static_cast<cricket::FakeIceTransport*>(
642 transport_controller_->GetDtlsTransport(kAudioMid1)->ice_transport());
643 fake_ice->SetCandidatesGatheringComplete();
644 fake_ice->SetConnectionCount(1);
645 // The connection stats will be failed if there is no active connection.
646 fake_ice->SetConnectionCount(0);
Alex Loiko9289eda2018-11-23 16:18:59647 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
Jonas Olsson1e87b4f2018-11-22 15:50:37648 EXPECT_EQ(1, connection_state_signal_count_);
Alex Loiko9289eda2018-11-23 16:18:59649 EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionFailed,
650 ice_connection_state_, kTimeout);
651 EXPECT_EQ(1, ice_connection_state_signal_count_);
Jonas Olssond8aa9f92018-11-09 09:51:09652 EXPECT_EQ_WAIT(PeerConnectionInterface::PeerConnectionState::kFailed,
653 combined_connection_state_, kTimeout);
Jonas Olsson635474e2018-10-18 13:58:17654 EXPECT_EQ(1, combined_connection_state_signal_count_);
Zhi Huange818b6e2018-02-22 23:26:27655}
656
Piotr (Peter) Slatala4eb41122018-11-01 14:26:03657TEST_F(JsepTransportControllerTest,
658 SignalConnectionStateConnectedNoMediaTransport) {
Zhi Huange818b6e2018-02-22 23:26:27659 CreateJsepTransportController(JsepTransportController::Config());
660 auto description = CreateSessionDescriptionWithoutBundle();
661 EXPECT_TRUE(transport_controller_
662 ->SetLocalDescription(SdpType::kOffer, description.get())
663 .ok());
664
665 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
666 transport_controller_->GetDtlsTransport(kAudioMid1));
667 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
668 transport_controller_->GetDtlsTransport(kVideoMid1));
669
670 // First, have one transport connect, and another fail, to ensure that
671 // the first transport connecting didn't trigger a "connected" state signal.
672 // We should only get a signal when all are connected.
673 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
674 fake_audio_dtls->SetWritable(true);
675 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
676 // Decrease the number of the connection to trigger the signal.
677 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
678 fake_video_dtls->fake_ice_transport()->SetConnectionCount(0);
679 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
680
Alex Loiko9289eda2018-11-23 16:18:59681 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
Jonas Olsson1e87b4f2018-11-22 15:50:37682 EXPECT_EQ(1, connection_state_signal_count_);
Alex Loiko9289eda2018-11-23 16:18:59683 EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionFailed,
684 ice_connection_state_, kTimeout);
Jonas Olsson6a8727b2018-12-07 12:11:44685 EXPECT_EQ(2, ice_connection_state_signal_count_);
Jonas Olssond8aa9f92018-11-09 09:51:09686 EXPECT_EQ_WAIT(PeerConnectionInterface::PeerConnectionState::kFailed,
687 combined_connection_state_, kTimeout);
Jonas Olsson6a8727b2018-12-07 12:11:44688 EXPECT_EQ(2, combined_connection_state_signal_count_);
Zhi Huange818b6e2018-02-22 23:26:27689
Jonas Olsson635474e2018-10-18 13:58:17690 fake_audio_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
691 fake_video_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
Zhi Huange818b6e2018-02-22 23:26:27692 // Set the connection count to be 2 and the cricket::FakeIceTransport will set
693 // the transport state to be STATE_CONNECTING.
694 fake_video_dtls->fake_ice_transport()->SetConnectionCount(2);
695 fake_video_dtls->SetWritable(true);
Alex Loiko9289eda2018-11-23 16:18:59696 EXPECT_EQ_WAIT(cricket::kIceConnectionConnected, connection_state_, kTimeout);
Jonas Olsson1e87b4f2018-11-22 15:50:37697 EXPECT_EQ(2, connection_state_signal_count_);
Alex Loiko9289eda2018-11-23 16:18:59698 EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionConnected,
699 ice_connection_state_, kTimeout);
Jonas Olsson6a8727b2018-12-07 12:11:44700 EXPECT_EQ(3, ice_connection_state_signal_count_);
Jonas Olssond8aa9f92018-11-09 09:51:09701 EXPECT_EQ_WAIT(PeerConnectionInterface::PeerConnectionState::kConnected,
702 combined_connection_state_, kTimeout);
Jonas Olsson6a8727b2018-12-07 12:11:44703 EXPECT_EQ(3, combined_connection_state_signal_count_);
Zhi Huange818b6e2018-02-22 23:26:27704}
705
706TEST_F(JsepTransportControllerTest, SignalConnectionStateComplete) {
707 CreateJsepTransportController(JsepTransportController::Config());
708 auto description = CreateSessionDescriptionWithoutBundle();
709 EXPECT_TRUE(transport_controller_
710 ->SetLocalDescription(SdpType::kOffer, description.get())
711 .ok());
712
713 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
714 transport_controller_->GetDtlsTransport(kAudioMid1));
715 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
716 transport_controller_->GetDtlsTransport(kVideoMid1));
717
718 // First, have one transport connect, and another fail, to ensure that
719 // the first transport connecting didn't trigger a "connected" state signal.
720 // We should only get a signal when all are connected.
Jonas Olsson6a8727b2018-12-07 12:11:44721 fake_audio_dtls->fake_ice_transport()->SetTransportState(
722 IceTransportState::kCompleted,
723 cricket::IceTransportState::STATE_COMPLETED);
Zhi Huange818b6e2018-02-22 23:26:27724 fake_audio_dtls->SetWritable(true);
725 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
Jonas Olsson6a8727b2018-12-07 12:11:44726
727 EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionChecking,
728 ice_connection_state_, kTimeout);
729 EXPECT_EQ(1, ice_connection_state_signal_count_);
730 EXPECT_EQ_WAIT(PeerConnectionInterface::PeerConnectionState::kConnecting,
731 combined_connection_state_, kTimeout);
732 EXPECT_EQ(1, combined_connection_state_signal_count_);
733
734 fake_video_dtls->fake_ice_transport()->SetTransportState(
735 IceTransportState::kFailed, cricket::IceTransportState::STATE_FAILED);
Zhi Huange818b6e2018-02-22 23:26:27736 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
737
Alex Loiko9289eda2018-11-23 16:18:59738 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
Jonas Olsson1e87b4f2018-11-22 15:50:37739 EXPECT_EQ(1, connection_state_signal_count_);
Alex Loiko9289eda2018-11-23 16:18:59740 EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionFailed,
741 ice_connection_state_, kTimeout);
Jonas Olsson6a8727b2018-12-07 12:11:44742 EXPECT_EQ(2, ice_connection_state_signal_count_);
Jonas Olssond8aa9f92018-11-09 09:51:09743 EXPECT_EQ_WAIT(PeerConnectionInterface::PeerConnectionState::kFailed,
744 combined_connection_state_, kTimeout);
Jonas Olsson6a8727b2018-12-07 12:11:44745 EXPECT_EQ(2, combined_connection_state_signal_count_);
Zhi Huange818b6e2018-02-22 23:26:27746
Jonas Olsson635474e2018-10-18 13:58:17747 fake_audio_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
748 fake_video_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
Zhi Huange818b6e2018-02-22 23:26:27749 // Set the connection count to be 1 and the cricket::FakeIceTransport will set
750 // the transport state to be STATE_COMPLETED.
Jonas Olsson6a8727b2018-12-07 12:11:44751 fake_video_dtls->fake_ice_transport()->SetTransportState(
752 IceTransportState::kCompleted,
753 cricket::IceTransportState::STATE_COMPLETED);
Zhi Huange818b6e2018-02-22 23:26:27754 fake_video_dtls->SetWritable(true);
Alex Loiko9289eda2018-11-23 16:18:59755 EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
Jonas Olsson7a6739e2019-01-15 15:31:55756 EXPECT_EQ(3, connection_state_signal_count_);
Alex Loiko9289eda2018-11-23 16:18:59757 EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionCompleted,
758 ice_connection_state_, kTimeout);
Jonas Olsson6a8727b2018-12-07 12:11:44759 EXPECT_EQ(3, ice_connection_state_signal_count_);
Jonas Olssond8aa9f92018-11-09 09:51:09760 EXPECT_EQ_WAIT(PeerConnectionInterface::PeerConnectionState::kConnected,
761 combined_connection_state_, kTimeout);
Jonas Olsson6a8727b2018-12-07 12:11:44762 EXPECT_EQ(3, combined_connection_state_signal_count_);
Zhi Huange818b6e2018-02-22 23:26:27763}
764
765TEST_F(JsepTransportControllerTest, SignalIceGatheringStateGathering) {
766 CreateJsepTransportController(JsepTransportController::Config());
767 auto description = CreateSessionDescriptionWithoutBundle();
768 EXPECT_TRUE(transport_controller_
769 ->SetLocalDescription(SdpType::kOffer, description.get())
770 .ok());
771
772 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
773 transport_controller_->GetDtlsTransport(kAudioMid1));
774 fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
775 // Should be in the gathering state as soon as any transport starts gathering.
776 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
777 EXPECT_EQ(1, gathering_state_signal_count_);
778}
779
780TEST_F(JsepTransportControllerTest, SignalIceGatheringStateComplete) {
781 CreateJsepTransportController(JsepTransportController::Config());
782 auto description = CreateSessionDescriptionWithoutBundle();
783 EXPECT_TRUE(transport_controller_
784 ->SetLocalDescription(SdpType::kOffer, description.get())
785 .ok());
786
787 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
788 transport_controller_->GetDtlsTransport(kAudioMid1));
789 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
790 transport_controller_->GetDtlsTransport(kVideoMid1));
791
792 fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
793 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
794 EXPECT_EQ(1, gathering_state_signal_count_);
795
796 // Have one transport finish gathering, to make sure gathering
797 // completion wasn't signalled if only one transport finished gathering.
798 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
799 EXPECT_EQ(1, gathering_state_signal_count_);
800
801 fake_video_dtls->fake_ice_transport()->MaybeStartGathering();
802 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
803 EXPECT_EQ(1, gathering_state_signal_count_);
804
805 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
806 EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
807 EXPECT_EQ(2, gathering_state_signal_count_);
808}
809
810// Test that when the last transport that hasn't finished connecting and/or
811// gathering is destroyed, the aggregate state jumps to "completed". This can
812// happen if, for example, we have an audio and video transport, the audio
813// transport completes, then we start bundling video on the audio transport.
814TEST_F(JsepTransportControllerTest,
815 SignalingWhenLastIncompleteTransportDestroyed) {
816 CreateJsepTransportController(JsepTransportController::Config());
817 auto description = CreateSessionDescriptionWithBundleGroup();
818 EXPECT_TRUE(transport_controller_
819 ->SetLocalDescription(SdpType::kOffer, description.get())
820 .ok());
821
822 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
823 transport_controller_->GetDtlsTransport(kAudioMid1));
824 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
825 transport_controller_->GetDtlsTransport(kVideoMid1));
826 EXPECT_NE(fake_audio_dtls, fake_video_dtls);
827
828 fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
829 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
830 EXPECT_EQ(1, gathering_state_signal_count_);
831
832 // Let the audio transport complete.
833 fake_audio_dtls->SetWritable(true);
834 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
835 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
Jonas Olsson635474e2018-10-18 13:58:17836 fake_audio_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
Zhi Huange818b6e2018-02-22 23:26:27837 EXPECT_EQ(1, gathering_state_signal_count_);
838
839 // Set the remote description and enable the bundle.
840 EXPECT_TRUE(transport_controller_
841 ->SetRemoteDescription(SdpType::kAnswer, description.get())
842 .ok());
843 // The BUNDLE should be enabled, the incomplete video transport should be
844 // deleted and the states shoud be updated.
845 fake_video_dtls = static_cast<FakeDtlsTransport*>(
846 transport_controller_->GetDtlsTransport(kVideoMid1));
847 EXPECT_EQ(fake_audio_dtls, fake_video_dtls);
Alex Loiko9289eda2018-11-23 16:18:59848 EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
849 EXPECT_EQ(PeerConnectionInterface::kIceConnectionCompleted,
850 ice_connection_state_);
Jonas Olsson635474e2018-10-18 13:58:17851 EXPECT_EQ(PeerConnectionInterface::PeerConnectionState::kConnected,
852 combined_connection_state_);
Zhi Huange818b6e2018-02-22 23:26:27853 EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
854 EXPECT_EQ(2, gathering_state_signal_count_);
855}
856
857TEST_F(JsepTransportControllerTest, SignalCandidatesGathered) {
858 CreateJsepTransportController(JsepTransportController::Config());
859 auto description = CreateSessionDescriptionWithBundleGroup();
860 EXPECT_TRUE(transport_controller_
861 ->SetLocalDescription(SdpType::kOffer, description.get())
862 .ok());
863 transport_controller_->MaybeStartGathering();
864
865 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
866 transport_controller_->GetDtlsTransport(kAudioMid1));
867 fake_audio_dtls->fake_ice_transport()->SignalCandidateGathered(
868 fake_audio_dtls->fake_ice_transport(), CreateCandidate(kAudioMid1, 1));
869 EXPECT_EQ_WAIT(1, candidates_signal_count_, kTimeout);
870 EXPECT_EQ(1u, candidates_[kAudioMid1].size());
871}
872
873TEST_F(JsepTransportControllerTest, IceSignalingOccursOnSignalingThread) {
874 network_thread_ = rtc::Thread::CreateWithSocketServer();
875 network_thread_->Start();
876 CreateJsepTransportController(JsepTransportController::Config(),
877 signaling_thread_, network_thread_.get(),
Mirko Bonadei970f2f72019-02-28 13:57:52878 /*port_allocator=*/nullptr);
Zhi Huange818b6e2018-02-22 23:26:27879 CreateLocalDescriptionAndCompleteConnectionOnNetworkThread();
880
881 // connecting --> connected --> completed
Alex Loiko9289eda2018-11-23 16:18:59882 EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
Zhi Huange818b6e2018-02-22 23:26:27883 EXPECT_EQ(2, connection_state_signal_count_);
884
885 // new --> gathering --> complete
886 EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
887 EXPECT_EQ(2, gathering_state_signal_count_);
888
889 EXPECT_EQ_WAIT(1u, candidates_[kAudioMid1].size(), kTimeout);
890 EXPECT_EQ_WAIT(1u, candidates_[kVideoMid1].size(), kTimeout);
891 EXPECT_EQ(2, candidates_signal_count_);
892
893 EXPECT_TRUE(!signaled_on_non_signaling_thread_);
894}
895
Zhi Huange818b6e2018-02-22 23:26:27896// Test that if the TransportController was created with the
897// |redetermine_role_on_ice_restart| parameter set to false, the role is *not*
898// redetermined on an ICE restart.
899TEST_F(JsepTransportControllerTest, IceRoleNotRedetermined) {
900 JsepTransportController::Config config;
901 config.redetermine_role_on_ice_restart = false;
902
903 CreateJsepTransportController(config);
904 // Let the |transport_controller_| be the controlled side initially.
Mirko Bonadei317a1f02019-09-17 15:06:18905 auto remote_offer = std::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 23:26:27906 AddAudioSection(remote_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
907 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
908 nullptr);
Mirko Bonadei317a1f02019-09-17 15:06:18909 auto local_answer = std::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 23:26:27910 AddAudioSection(local_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
911 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
912 nullptr);
913
914 EXPECT_TRUE(transport_controller_
915 ->SetRemoteDescription(SdpType::kOffer, remote_offer.get())
916 .ok());
917 EXPECT_TRUE(transport_controller_
918 ->SetLocalDescription(SdpType::kAnswer, local_answer.get())
919 .ok());
920
921 auto fake_dtls = static_cast<FakeDtlsTransport*>(
922 transport_controller_->GetDtlsTransport(kAudioMid1));
923 EXPECT_EQ(cricket::ICEROLE_CONTROLLED,
924 fake_dtls->fake_ice_transport()->GetIceRole());
925
926 // New offer will trigger the ICE restart.
Mirko Bonadei317a1f02019-09-17 15:06:18927 auto restart_local_offer = std::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 23:26:27928 AddAudioSection(restart_local_offer.get(), kAudioMid1, kIceUfrag3, kIcePwd3,
929 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
930 nullptr);
931 EXPECT_TRUE(
932 transport_controller_
933 ->SetLocalDescription(SdpType::kOffer, restart_local_offer.get())
934 .ok());
935 EXPECT_EQ(cricket::ICEROLE_CONTROLLED,
936 fake_dtls->fake_ice_transport()->GetIceRole());
937}
938
939// Tests ICE-Lite mode in remote answer.
940TEST_F(JsepTransportControllerTest, SetIceRoleWhenIceLiteInRemoteAnswer) {
941 CreateJsepTransportController(JsepTransportController::Config());
Mirko Bonadei317a1f02019-09-17 15:06:18942 auto local_offer = std::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 23:26:27943 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
944 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
945 nullptr);
946 EXPECT_TRUE(transport_controller_
947 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
948 .ok());
949 auto fake_dtls = static_cast<FakeDtlsTransport*>(
950 transport_controller_->GetDtlsTransport(kAudioMid1));
951 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
952 fake_dtls->fake_ice_transport()->GetIceRole());
953 EXPECT_EQ(cricket::ICEMODE_FULL,
954 fake_dtls->fake_ice_transport()->remote_ice_mode());
955
Mirko Bonadei317a1f02019-09-17 15:06:18956 auto remote_answer = std::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 23:26:27957 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
958 cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_PASSIVE,
959 nullptr);
960 EXPECT_TRUE(transport_controller_
961 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
962 .ok());
963 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
964 fake_dtls->fake_ice_transport()->GetIceRole());
965 EXPECT_EQ(cricket::ICEMODE_LITE,
966 fake_dtls->fake_ice_transport()->remote_ice_mode());
967}
968
969// Tests that the ICE role remains "controlling" if a subsequent offer that
970// does an ICE restart is received from an ICE lite endpoint. Regression test
971// for: https://crbug.com/710760
972TEST_F(JsepTransportControllerTest,
973 IceRoleIsControllingAfterIceRestartFromIceLiteEndpoint) {
974 CreateJsepTransportController(JsepTransportController::Config());
Mirko Bonadei317a1f02019-09-17 15:06:18975 auto remote_offer = std::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 23:26:27976 AddAudioSection(remote_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
977 cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_ACTPASS,
978 nullptr);
Mirko Bonadei317a1f02019-09-17 15:06:18979 auto local_answer = std::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 23:26:27980 AddAudioSection(local_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
981 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
982 nullptr);
983 // Initial Offer/Answer exchange. If the remote offerer is ICE-Lite, then the
984 // local side is the controlling.
985 EXPECT_TRUE(transport_controller_
986 ->SetRemoteDescription(SdpType::kOffer, remote_offer.get())
987 .ok());
988 EXPECT_TRUE(transport_controller_
989 ->SetLocalDescription(SdpType::kAnswer, local_answer.get())
990 .ok());
991 auto fake_dtls = static_cast<FakeDtlsTransport*>(
992 transport_controller_->GetDtlsTransport(kAudioMid1));
993 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
994 fake_dtls->fake_ice_transport()->GetIceRole());
995
996 // In the subsequence remote offer triggers an ICE restart.
Mirko Bonadei317a1f02019-09-17 15:06:18997 auto remote_offer2 = std::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 23:26:27998 AddAudioSection(remote_offer2.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
999 cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_ACTPASS,
1000 nullptr);
Mirko Bonadei317a1f02019-09-17 15:06:181001 auto local_answer2 = std::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 23:26:271002 AddAudioSection(local_answer2.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1003 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1004 nullptr);
1005 EXPECT_TRUE(transport_controller_
1006 ->SetRemoteDescription(SdpType::kOffer, remote_offer2.get())
1007 .ok());
1008 EXPECT_TRUE(transport_controller_
1009 ->SetLocalDescription(SdpType::kAnswer, local_answer2.get())
1010 .ok());
1011 fake_dtls = static_cast<FakeDtlsTransport*>(
1012 transport_controller_->GetDtlsTransport(kAudioMid1));
1013 // The local side is still the controlling role since the remote side is using
1014 // ICE-Lite.
1015 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
1016 fake_dtls->fake_ice_transport()->GetIceRole());
1017}
1018
1019// Tests that the SDP has more than one audio/video m= sections.
1020TEST_F(JsepTransportControllerTest, MultipleMediaSectionsOfSameTypeWithBundle) {
1021 CreateJsepTransportController(JsepTransportController::Config());
1022 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1023 bundle_group.AddContentName(kAudioMid1);
1024 bundle_group.AddContentName(kAudioMid2);
1025 bundle_group.AddContentName(kVideoMid1);
1026 bundle_group.AddContentName(kDataMid1);
1027
Mirko Bonadei317a1f02019-09-17 15:06:181028 auto local_offer = std::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 23:26:271029 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1030 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1031 nullptr);
1032 AddAudioSection(local_offer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
1033 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1034 nullptr);
1035 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1036 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1037 nullptr);
1038 AddDataSection(local_offer.get(), kDataMid1,
1039 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
1040 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1041 nullptr);
1042
Mirko Bonadei317a1f02019-09-17 15:06:181043 auto remote_answer = std::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 23:26:271044 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1045 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1046 nullptr);
1047 AddAudioSection(remote_answer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
1048 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1049 nullptr);
1050 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1051 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1052 nullptr);
1053 AddDataSection(remote_answer.get(), kDataMid1,
1054 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
1055 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1056 nullptr);
1057
1058 local_offer->AddGroup(bundle_group);
1059 remote_answer->AddGroup(bundle_group);
1060
1061 EXPECT_TRUE(transport_controller_
1062 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1063 .ok());
1064 EXPECT_TRUE(transport_controller_
1065 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1066 .ok());
1067 // Verify that all the sections are bundled on kAudio1.
1068 auto transport1 = transport_controller_->GetRtpTransport(kAudioMid1);
1069 auto transport2 = transport_controller_->GetRtpTransport(kAudioMid2);
1070 auto transport3 = transport_controller_->GetRtpTransport(kVideoMid1);
1071 auto transport4 = transport_controller_->GetRtpTransport(kDataMid1);
1072 EXPECT_EQ(transport1, transport2);
1073 EXPECT_EQ(transport1, transport3);
1074 EXPECT_EQ(transport1, transport4);
1075
Harald Alvestrandad88c882018-11-28 15:47:461076 EXPECT_EQ(transport_controller_->LookupDtlsTransportByMid(kAudioMid1),
1077 transport_controller_->LookupDtlsTransportByMid(kVideoMid1));
1078
Zhi Huange818b6e2018-02-22 23:26:271079 // Verify the OnRtpTransport/DtlsTransportChanged signals are fired correctly.
1080 auto it = changed_rtp_transport_by_mid_.find(kAudioMid2);
1081 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1082 EXPECT_EQ(transport1, it->second);
1083 it = changed_rtp_transport_by_mid_.find(kAudioMid2);
1084 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1085 EXPECT_EQ(transport1, it->second);
1086 it = changed_rtp_transport_by_mid_.find(kVideoMid1);
1087 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1088 EXPECT_EQ(transport1, it->second);
1089 // Verify the DtlsTransport for the SCTP data channel is reset correctly.
1090 auto it2 = changed_dtls_transport_by_mid_.find(kDataMid1);
1091 ASSERT_TRUE(it2 != changed_dtls_transport_by_mid_.end());
Zhi Huange818b6e2018-02-22 23:26:271092}
1093
1094// Tests that only a subset of all the m= sections are bundled.
1095TEST_F(JsepTransportControllerTest, BundleSubsetOfMediaSections) {
1096 CreateJsepTransportController(JsepTransportController::Config());
1097 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1098 bundle_group.AddContentName(kAudioMid1);
1099 bundle_group.AddContentName(kVideoMid1);
1100
Mirko Bonadei317a1f02019-09-17 15:06:181101 auto local_offer = std::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 23:26:271102 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1103 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1104 nullptr);
1105 AddAudioSection(local_offer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
1106 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1107 nullptr);
1108 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1109 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1110 nullptr);
1111
Mirko Bonadei317a1f02019-09-17 15:06:181112 auto remote_answer = std::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 23:26:271113 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1114 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1115 nullptr);
1116 AddAudioSection(remote_answer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
1117 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1118 nullptr);
1119 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1120 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1121 nullptr);
1122
1123 local_offer->AddGroup(bundle_group);
1124 remote_answer->AddGroup(bundle_group);
1125 EXPECT_TRUE(transport_controller_
1126 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1127 .ok());
1128 EXPECT_TRUE(transport_controller_
1129 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1130 .ok());
1131
1132 // Verifiy that only |kAudio1| and |kVideo1| are bundled.
1133 auto transport1 = transport_controller_->GetRtpTransport(kAudioMid1);
1134 auto transport2 = transport_controller_->GetRtpTransport(kAudioMid2);
1135 auto transport3 = transport_controller_->GetRtpTransport(kVideoMid1);
1136 EXPECT_NE(transport1, transport2);
1137 EXPECT_EQ(transport1, transport3);
1138
1139 auto it = changed_rtp_transport_by_mid_.find(kVideoMid1);
1140 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1141 EXPECT_EQ(transport1, it->second);
1142 it = changed_rtp_transport_by_mid_.find(kAudioMid2);
Zhi Huangd2248f82018-04-10 21:41:031143 EXPECT_TRUE(transport2 == it->second);
Zhi Huange818b6e2018-02-22 23:26:271144}
1145
1146// Tests that the initial offer/answer only have data section and audio/video
1147// sections are added in the subsequent offer.
1148TEST_F(JsepTransportControllerTest, BundleOnDataSectionInSubsequentOffer) {
1149 CreateJsepTransportController(JsepTransportController::Config());
1150 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1151 bundle_group.AddContentName(kDataMid1);
1152
Mirko Bonadei317a1f02019-09-17 15:06:181153 auto local_offer = std::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 23:26:271154 AddDataSection(local_offer.get(), kDataMid1,
1155 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
1156 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1157 nullptr);
Mirko Bonadei317a1f02019-09-17 15:06:181158 auto remote_answer = std::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 23:26:271159 AddDataSection(remote_answer.get(), kDataMid1,
1160 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
1161 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1162 nullptr);
1163 local_offer->AddGroup(bundle_group);
1164 remote_answer->AddGroup(bundle_group);
1165
1166 EXPECT_TRUE(transport_controller_
1167 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1168 .ok());
1169 EXPECT_TRUE(transport_controller_
1170 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1171 .ok());
1172 auto data_transport = transport_controller_->GetRtpTransport(kDataMid1);
1173
1174 // Add audio/video sections in subsequent offer.
1175 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1176 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1177 nullptr);
1178 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1179 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1180 nullptr);
1181 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1182 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1183 nullptr);
1184 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1185 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1186 nullptr);
1187
1188 // Reset the bundle group and do another offer/answer exchange.
1189 bundle_group.AddContentName(kAudioMid1);
1190 bundle_group.AddContentName(kVideoMid1);
1191 local_offer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1192 remote_answer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1193 local_offer->AddGroup(bundle_group);
1194 remote_answer->AddGroup(bundle_group);
1195
1196 EXPECT_TRUE(transport_controller_
1197 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1198 .ok());
1199 EXPECT_TRUE(transport_controller_
1200 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1201 .ok());
1202
1203 auto audio_transport = transport_controller_->GetRtpTransport(kAudioMid1);
1204 auto video_transport = transport_controller_->GetRtpTransport(kVideoMid1);
1205 EXPECT_EQ(data_transport, audio_transport);
1206 EXPECT_EQ(data_transport, video_transport);
1207}
1208
1209TEST_F(JsepTransportControllerTest, VideoDataRejectedInAnswer) {
1210 CreateJsepTransportController(JsepTransportController::Config());
1211 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1212 bundle_group.AddContentName(kAudioMid1);
1213 bundle_group.AddContentName(kVideoMid1);
1214 bundle_group.AddContentName(kDataMid1);
1215
Mirko Bonadei317a1f02019-09-17 15:06:181216 auto local_offer = std::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 23:26:271217 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1218 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1219 nullptr);
1220 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1221 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1222 nullptr);
1223 AddDataSection(local_offer.get(), kDataMid1,
1224 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1225 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1226 nullptr);
1227
Mirko Bonadei317a1f02019-09-17 15:06:181228 auto remote_answer = std::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 23:26:271229 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1230 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1231 nullptr);
1232 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1233 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1234 nullptr);
1235 AddDataSection(remote_answer.get(), kDataMid1,
1236 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1237 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1238 nullptr);
1239 // Reject video and data section.
1240 remote_answer->contents()[1].rejected = true;
1241 remote_answer->contents()[2].rejected = true;
1242
1243 local_offer->AddGroup(bundle_group);
1244 remote_answer->AddGroup(bundle_group);
1245
1246 EXPECT_TRUE(transport_controller_
1247 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1248 .ok());
1249 EXPECT_TRUE(transport_controller_
1250 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1251 .ok());
1252
1253 // Verify the RtpTransport/DtlsTransport is destroyed correctly.
1254 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kVideoMid1));
1255 EXPECT_EQ(nullptr, transport_controller_->GetDtlsTransport(kDataMid1));
1256 // Verify the signals are fired correctly.
1257 auto it = changed_rtp_transport_by_mid_.find(kVideoMid1);
1258 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1259 EXPECT_EQ(nullptr, it->second);
1260 auto it2 = changed_dtls_transport_by_mid_.find(kDataMid1);
1261 ASSERT_TRUE(it2 != changed_dtls_transport_by_mid_.end());
1262 EXPECT_EQ(nullptr, it2->second);
1263}
1264
1265// Tests that changing the bundled MID in subsequent offer/answer exchange is
1266// not supported.
1267// TODO(bugs.webrtc.org/6704): Change this test to expect success once issue is
1268// fixed
1269TEST_F(JsepTransportControllerTest, ChangeBundledMidNotSupported) {
1270 CreateJsepTransportController(JsepTransportController::Config());
1271 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1272 bundle_group.AddContentName(kAudioMid1);
1273 bundle_group.AddContentName(kVideoMid1);
1274
Mirko Bonadei317a1f02019-09-17 15:06:181275 auto local_offer = std::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 23:26:271276 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1277 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1278 nullptr);
1279 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1280 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1281 nullptr);
1282
Mirko Bonadei317a1f02019-09-17 15:06:181283 auto remote_answer = std::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 23:26:271284 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1285 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1286 nullptr);
1287 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1288 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1289 nullptr);
1290
1291 local_offer->AddGroup(bundle_group);
1292 remote_answer->AddGroup(bundle_group);
1293 EXPECT_TRUE(transport_controller_
1294 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1295 .ok());
1296 EXPECT_TRUE(transport_controller_
1297 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1298 .ok());
1299 EXPECT_EQ(transport_controller_->GetRtpTransport(kAudioMid1),
1300 transport_controller_->GetRtpTransport(kVideoMid1));
1301
1302 // Reorder the bundle group.
1303 EXPECT_TRUE(bundle_group.RemoveContentName(kAudioMid1));
1304 bundle_group.AddContentName(kAudioMid1);
1305 // The answerer uses the new bundle group and now the bundle mid is changed to
1306 // |kVideo1|.
1307 remote_answer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1308 remote_answer->AddGroup(bundle_group);
1309 EXPECT_TRUE(transport_controller_
1310 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1311 .ok());
1312 EXPECT_FALSE(transport_controller_
1313 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1314 .ok());
1315}
Zhi Huange830e682018-03-30 17:48:351316// Test that rejecting only the first m= section of a BUNDLE group is treated as
1317// an error, but rejecting all of them works as expected.
1318TEST_F(JsepTransportControllerTest, RejectFirstContentInBundleGroup) {
1319 CreateJsepTransportController(JsepTransportController::Config());
1320 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1321 bundle_group.AddContentName(kAudioMid1);
1322 bundle_group.AddContentName(kVideoMid1);
1323 bundle_group.AddContentName(kDataMid1);
1324
Mirko Bonadei317a1f02019-09-17 15:06:181325 auto local_offer = std::make_unique<cricket::SessionDescription>();
Zhi Huange830e682018-03-30 17:48:351326 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1327 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1328 nullptr);
1329 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1330 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1331 nullptr);
1332 AddDataSection(local_offer.get(), kDataMid1,
1333 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1334 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1335 nullptr);
1336
Mirko Bonadei317a1f02019-09-17 15:06:181337 auto remote_answer = std::make_unique<cricket::SessionDescription>();
Zhi Huange830e682018-03-30 17:48:351338 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1339 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1340 nullptr);
1341 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1342 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1343 nullptr);
1344 AddDataSection(remote_answer.get(), kDataMid1,
1345 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1346 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1347 nullptr);
1348 // Reject audio content in answer.
1349 remote_answer->contents()[0].rejected = true;
1350
1351 local_offer->AddGroup(bundle_group);
1352 remote_answer->AddGroup(bundle_group);
1353
1354 EXPECT_TRUE(transport_controller_
1355 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1356 .ok());
1357 EXPECT_FALSE(transport_controller_
1358 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1359 .ok());
1360
1361 // Reject all the contents.
1362 remote_answer->contents()[1].rejected = true;
1363 remote_answer->contents()[2].rejected = true;
1364 EXPECT_TRUE(transport_controller_
1365 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1366 .ok());
1367 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kAudioMid1));
1368 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kVideoMid1));
1369 EXPECT_EQ(nullptr, transport_controller_->GetDtlsTransport(kDataMid1));
1370}
1371
1372// Tests that applying non-RTCP-mux offer would fail when kRtcpMuxPolicyRequire
1373// is used.
1374TEST_F(JsepTransportControllerTest, ApplyNonRtcpMuxOfferWhenMuxingRequired) {
1375 JsepTransportController::Config config;
1376 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
1377 CreateJsepTransportController(config);
Mirko Bonadei317a1f02019-09-17 15:06:181378 auto local_offer = std::make_unique<cricket::SessionDescription>();
Zhi Huange830e682018-03-30 17:48:351379 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1380 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1381 nullptr);
1382
1383 local_offer->contents()[0].media_description()->set_rtcp_mux(false);
1384 // Applying a non-RTCP-mux offer is expected to fail.
1385 EXPECT_FALSE(transport_controller_
1386 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1387 .ok());
1388}
1389
1390// Tests that applying non-RTCP-mux answer would fail when kRtcpMuxPolicyRequire
1391// is used.
1392TEST_F(JsepTransportControllerTest, ApplyNonRtcpMuxAnswerWhenMuxingRequired) {
1393 JsepTransportController::Config config;
1394 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
1395 CreateJsepTransportController(config);
Mirko Bonadei317a1f02019-09-17 15:06:181396 auto local_offer = std::make_unique<cricket::SessionDescription>();
Zhi Huange830e682018-03-30 17:48:351397 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1398 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1399 nullptr);
1400 EXPECT_TRUE(transport_controller_
1401 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1402 .ok());
1403
Mirko Bonadei317a1f02019-09-17 15:06:181404 auto remote_answer = std::make_unique<cricket::SessionDescription>();
Zhi Huange830e682018-03-30 17:48:351405 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1406 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1407 nullptr);
1408 // Applying a non-RTCP-mux answer is expected to fail.
1409 remote_answer->contents()[0].media_description()->set_rtcp_mux(false);
1410 EXPECT_FALSE(transport_controller_
1411 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1412 .ok());
1413}
Zhi Huange818b6e2018-02-22 23:26:271414
Zhi Huangd2248f82018-04-10 21:41:031415// This tests that the BUNDLE group in answer should be a subset of the offered
1416// group.
1417TEST_F(JsepTransportControllerTest,
1418 AddContentToBundleGroupInAnswerNotSupported) {
1419 CreateJsepTransportController(JsepTransportController::Config());
1420 auto local_offer = CreateSessionDescriptionWithoutBundle();
1421 auto remote_answer = CreateSessionDescriptionWithoutBundle();
1422
1423 cricket::ContentGroup offer_bundle_group(cricket::GROUP_TYPE_BUNDLE);
1424 offer_bundle_group.AddContentName(kAudioMid1);
1425 local_offer->AddGroup(offer_bundle_group);
1426
1427 cricket::ContentGroup answer_bundle_group(cricket::GROUP_TYPE_BUNDLE);
1428 answer_bundle_group.AddContentName(kAudioMid1);
1429 answer_bundle_group.AddContentName(kVideoMid1);
1430 remote_answer->AddGroup(answer_bundle_group);
1431 EXPECT_TRUE(transport_controller_
1432 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1433 .ok());
1434 EXPECT_FALSE(transport_controller_
1435 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1436 .ok());
1437}
1438
1439// This tests that the BUNDLE group with non-existing MID should be rejectd.
1440TEST_F(JsepTransportControllerTest, RejectBundleGroupWithNonExistingMid) {
1441 CreateJsepTransportController(JsepTransportController::Config());
1442 auto local_offer = CreateSessionDescriptionWithoutBundle();
1443 auto remote_answer = CreateSessionDescriptionWithoutBundle();
1444
1445 cricket::ContentGroup invalid_bundle_group(cricket::GROUP_TYPE_BUNDLE);
1446 // The BUNDLE group is invalid because there is no data section in the
1447 // description.
1448 invalid_bundle_group.AddContentName(kDataMid1);
1449 local_offer->AddGroup(invalid_bundle_group);
1450 remote_answer->AddGroup(invalid_bundle_group);
1451
1452 EXPECT_FALSE(transport_controller_
1453 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1454 .ok());
1455 EXPECT_FALSE(transport_controller_
1456 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1457 .ok());
1458}
1459
1460// This tests that an answer shouldn't be able to remove an m= section from an
1461// established group without rejecting it.
1462TEST_F(JsepTransportControllerTest, RemoveContentFromBundleGroup) {
1463 CreateJsepTransportController(JsepTransportController::Config());
1464
1465 auto local_offer = CreateSessionDescriptionWithBundleGroup();
1466 auto remote_answer = CreateSessionDescriptionWithBundleGroup();
1467 EXPECT_TRUE(transport_controller_
1468 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1469 .ok());
1470 EXPECT_TRUE(transport_controller_
1471 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1472 .ok());
1473
1474 // Do an re-offer/answer.
1475 EXPECT_TRUE(transport_controller_
1476 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1477 .ok());
1478 auto new_answer = CreateSessionDescriptionWithoutBundle();
1479 cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
1480 // The answer removes video from the BUNDLE group without rejecting it is
1481 // invalid.
1482 new_bundle_group.AddContentName(kAudioMid1);
1483 new_answer->AddGroup(new_bundle_group);
1484
1485 // Applying invalid answer is expected to fail.
1486 EXPECT_FALSE(transport_controller_
1487 ->SetRemoteDescription(SdpType::kAnswer, new_answer.get())
1488 .ok());
1489
1490 // Rejected the video content.
1491 auto video_content = new_answer->GetContentByName(kVideoMid1);
1492 ASSERT_TRUE(video_content);
1493 video_content->rejected = true;
1494 EXPECT_TRUE(transport_controller_
1495 ->SetRemoteDescription(SdpType::kAnswer, new_answer.get())
1496 .ok());
1497}
1498
Steve Anton2bed3972019-01-05 01:04:301499// Test that the JsepTransportController can process a new local and remote
1500// description that changes the tagged BUNDLE group with the max-bundle policy
1501// specified.
1502// This is a regression test for bugs.webrtc.org/9954
1503TEST_F(JsepTransportControllerTest, ChangeTaggedMediaSectionMaxBundle) {
1504 CreateJsepTransportController(JsepTransportController::Config());
1505
Mirko Bonadei317a1f02019-09-17 15:06:181506 auto local_offer = std::make_unique<cricket::SessionDescription>();
Steve Anton2bed3972019-01-05 01:04:301507 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1508 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1509 nullptr);
1510 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1511 bundle_group.AddContentName(kAudioMid1);
1512 local_offer->AddGroup(bundle_group);
1513 EXPECT_TRUE(transport_controller_
1514 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1515 .ok());
1516
1517 std::unique_ptr<cricket::SessionDescription> remote_answer(
Harald Alvestrand4d7160e2019-04-12 05:01:291518 local_offer->Clone());
Steve Anton2bed3972019-01-05 01:04:301519 EXPECT_TRUE(transport_controller_
1520 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1521 .ok());
1522
1523 std::unique_ptr<cricket::SessionDescription> local_reoffer(
Harald Alvestrand4d7160e2019-04-12 05:01:291524 local_offer->Clone());
Steve Anton2bed3972019-01-05 01:04:301525 local_reoffer->contents()[0].rejected = true;
1526 AddVideoSection(local_reoffer.get(), kVideoMid1, kIceUfrag1, kIcePwd1,
1527 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1528 nullptr);
1529 local_reoffer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1530 cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
1531 new_bundle_group.AddContentName(kVideoMid1);
1532 local_reoffer->AddGroup(new_bundle_group);
1533
1534 EXPECT_TRUE(transport_controller_
1535 ->SetLocalDescription(SdpType::kOffer, local_reoffer.get())
1536 .ok());
1537
1538 std::unique_ptr<cricket::SessionDescription> remote_reanswer(
Harald Alvestrand4d7160e2019-04-12 05:01:291539 local_reoffer->Clone());
Steve Anton2bed3972019-01-05 01:04:301540 EXPECT_TRUE(
1541 transport_controller_
1542 ->SetRemoteDescription(SdpType::kAnswer, remote_reanswer.get())
1543 .ok());
1544}
1545
Zhi Huange818b6e2018-02-22 23:26:271546} // namespace webrtc