blob: 018140d6a7f49cbad1f5cb679bc2f2a1d147d0b9 [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
11#include <map>
12#include <memory>
13
Steve Anton2bed3972019-01-04 17:04:30 -080014#include "absl/memory/memory.h"
Piotr (Peter) Slatalacc8e8bb2018-11-15 08:26:19 -080015#include "api/media_transport_interface.h"
Anton Sukhanov7940da02018-10-10 10:34:49 -070016#include "api/test/fake_media_transport.h"
Steve Anton10542f22019-01-11 09:11:00 -080017#include "p2p/base/fake_dtls_transport.h"
18#include "p2p/base/fake_ice_transport.h"
19#include "p2p/base/transport_factory_interface.h"
20#include "p2p/base/transport_info.h"
21#include "pc/jsep_transport_controller.h"
Zhi Huange818b6e2018-02-22 15:26:27 -080022#include "rtc_base/gunit.h"
Zhi Huange818b6e2018-02-22 15:26:27 -080023#include "rtc_base/thread.h"
24#include "test/gtest.h"
25
26using cricket::FakeDtlsTransport;
27using cricket::Candidate;
28using cricket::Candidates;
29using webrtc::SdpType;
30
31static const int kTimeout = 100;
32static const char kIceUfrag1[] = "u0001";
33static const char kIcePwd1[] = "TESTICEPWD00000000000001";
34static const char kIceUfrag2[] = "u0002";
35static const char kIcePwd2[] = "TESTICEPWD00000000000002";
36static const char kIceUfrag3[] = "u0003";
37static const char kIcePwd3[] = "TESTICEPWD00000000000003";
38static const char kAudioMid1[] = "audio1";
39static const char kAudioMid2[] = "audio2";
40static const char kVideoMid1[] = "video1";
41static const char kVideoMid2[] = "video2";
42static const char kDataMid1[] = "data1";
43
44namespace webrtc {
45
Piotr (Peter) Slatala9f956252018-10-31 08:25:26 -070046namespace {
47
48// Media transport factory requires crypto settings to be present in order to
49// create media transport.
50void AddCryptoSettings(cricket::SessionDescription* description) {
51 for (auto& content : description->contents()) {
52 content.media_description()->AddCrypto(cricket::CryptoParams(
53 /*t=*/0, std::string(rtc::CS_AES_CM_128_HMAC_SHA1_80),
54 "inline:YUJDZGVmZ2hpSktMbW9QUXJzVHVWd3l6MTIzNDU2", ""));
55 }
56}
57
58} // namespace
59
Zhi Huange818b6e2018-02-22 15:26:27 -080060class FakeTransportFactory : public cricket::TransportFactoryInterface {
61 public:
62 std::unique_ptr<cricket::IceTransportInternal> CreateIceTransport(
63 const std::string& transport_name,
64 int component) override {
Karl Wiberg918f50c2018-07-05 11:40:33 +020065 return absl::make_unique<cricket::FakeIceTransport>(transport_name,
66 component);
Zhi Huange818b6e2018-02-22 15:26:27 -080067 }
68
69 std::unique_ptr<cricket::DtlsTransportInternal> CreateDtlsTransport(
70 std::unique_ptr<cricket::IceTransportInternal> ice,
Benjamin Wrighta54daf12018-10-11 15:33:17 -070071 const webrtc::CryptoOptions& crypto_options) override {
Zhi Huange818b6e2018-02-22 15:26:27 -080072 std::unique_ptr<cricket::FakeIceTransport> fake_ice(
73 static_cast<cricket::FakeIceTransport*>(ice.release()));
Karl Wiberg918f50c2018-07-05 11:40:33 +020074 return absl::make_unique<FakeDtlsTransport>(std::move(fake_ice));
Zhi Huange818b6e2018-02-22 15:26:27 -080075 }
76};
77
Zhi Huang365381f2018-04-13 16:44:34 -070078class JsepTransportControllerTest : public JsepTransportController::Observer,
79 public testing::Test,
Zhi Huange818b6e2018-02-22 15:26:27 -080080 public sigslot::has_slots<> {
81 public:
82 JsepTransportControllerTest() : signaling_thread_(rtc::Thread::Current()) {
Karl Wiberg918f50c2018-07-05 11:40:33 +020083 fake_transport_factory_ = absl::make_unique<FakeTransportFactory>();
Zhi Huange818b6e2018-02-22 15:26:27 -080084 }
85
86 void CreateJsepTransportController(
87 JsepTransportController::Config config,
88 rtc::Thread* signaling_thread = rtc::Thread::Current(),
89 rtc::Thread* network_thread = rtc::Thread::Current(),
90 cricket::PortAllocator* port_allocator = nullptr) {
Zhi Huang365381f2018-04-13 16:44:34 -070091 config.transport_observer = this;
Zhi Huange818b6e2018-02-22 15:26:27 -080092 // The tests only works with |fake_transport_factory|;
93 config.external_transport_factory = fake_transport_factory_.get();
Zach Steine20867f2018-08-02 13:20:15 -070094 // TODO(zstein): Provide an AsyncResolverFactory once it is required.
Karl Wiberg918f50c2018-07-05 11:40:33 +020095 transport_controller_ = absl::make_unique<JsepTransportController>(
Zach Steine20867f2018-08-02 13:20:15 -070096 signaling_thread, network_thread, port_allocator, nullptr, config);
Zhi Huange818b6e2018-02-22 15:26:27 -080097 ConnectTransportControllerSignals();
98 }
99
100 void ConnectTransportControllerSignals() {
101 transport_controller_->SignalIceConnectionState.connect(
102 this, &JsepTransportControllerTest::OnConnectionState);
Alex Loiko9289eda2018-11-23 16:18:59 +0000103 transport_controller_->SignalStandardizedIceConnectionState.connect(
104 this, &JsepTransportControllerTest::OnStandardizedIceConnectionState);
Jonas Olsson635474e2018-10-18 15:58:17 +0200105 transport_controller_->SignalConnectionState.connect(
106 this, &JsepTransportControllerTest::OnCombinedConnectionState);
Zhi Huange818b6e2018-02-22 15:26:27 -0800107 transport_controller_->SignalIceGatheringState.connect(
108 this, &JsepTransportControllerTest::OnGatheringState);
109 transport_controller_->SignalIceCandidatesGathered.connect(
110 this, &JsepTransportControllerTest::OnCandidatesGathered);
Zhi Huange818b6e2018-02-22 15:26:27 -0800111 }
112
113 std::unique_ptr<cricket::SessionDescription>
114 CreateSessionDescriptionWithoutBundle() {
Karl Wiberg918f50c2018-07-05 11:40:33 +0200115 auto description = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800116 AddAudioSection(description.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
117 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
118 nullptr);
119 AddVideoSection(description.get(), kVideoMid1, kIceUfrag1, kIcePwd1,
120 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
121 nullptr);
122 return description;
123 }
124
125 std::unique_ptr<cricket::SessionDescription>
126 CreateSessionDescriptionWithBundleGroup() {
127 auto description = CreateSessionDescriptionWithoutBundle();
128 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
129 bundle_group.AddContentName(kAudioMid1);
130 bundle_group.AddContentName(kVideoMid1);
131 description->AddGroup(bundle_group);
132
133 return description;
134 }
135
136 void AddAudioSection(cricket::SessionDescription* description,
137 const std::string& mid,
138 const std::string& ufrag,
139 const std::string& pwd,
140 cricket::IceMode ice_mode,
141 cricket::ConnectionRole conn_role,
142 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
143 std::unique_ptr<cricket::AudioContentDescription> audio(
144 new cricket::AudioContentDescription());
Zhi Huange830e682018-03-30 10:48:35 -0700145 // Set RTCP-mux to be true because the default policy is "mux required".
146 audio->set_rtcp_mux(true);
Zhi Huange818b6e2018-02-22 15:26:27 -0800147 description->AddContent(mid, cricket::MediaProtocolType::kRtp,
148 /*rejected=*/false, audio.release());
149 AddTransportInfo(description, mid, ufrag, pwd, ice_mode, conn_role, cert);
150 }
151
152 void AddVideoSection(cricket::SessionDescription* description,
153 const std::string& mid,
154 const std::string& ufrag,
155 const std::string& pwd,
156 cricket::IceMode ice_mode,
157 cricket::ConnectionRole conn_role,
158 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
159 std::unique_ptr<cricket::VideoContentDescription> video(
160 new cricket::VideoContentDescription());
Zhi Huange830e682018-03-30 10:48:35 -0700161 // Set RTCP-mux to be true because the default policy is "mux required".
162 video->set_rtcp_mux(true);
Zhi Huange818b6e2018-02-22 15:26:27 -0800163 description->AddContent(mid, cricket::MediaProtocolType::kRtp,
164 /*rejected=*/false, video.release());
165 AddTransportInfo(description, mid, ufrag, pwd, ice_mode, conn_role, cert);
166 }
167
168 void AddDataSection(cricket::SessionDescription* description,
169 const std::string& mid,
170 cricket::MediaProtocolType protocol_type,
171 const std::string& ufrag,
172 const std::string& pwd,
173 cricket::IceMode ice_mode,
174 cricket::ConnectionRole conn_role,
175 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
176 std::unique_ptr<cricket::DataContentDescription> data(
177 new cricket::DataContentDescription());
Zhi Huange830e682018-03-30 10:48:35 -0700178 data->set_rtcp_mux(true);
Zhi Huange818b6e2018-02-22 15:26:27 -0800179 description->AddContent(mid, protocol_type,
180 /*rejected=*/false, data.release());
181 AddTransportInfo(description, mid, ufrag, pwd, ice_mode, conn_role, cert);
182 }
183
184 void AddTransportInfo(cricket::SessionDescription* description,
185 const std::string& mid,
186 const std::string& ufrag,
187 const std::string& pwd,
188 cricket::IceMode ice_mode,
189 cricket::ConnectionRole conn_role,
190 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
191 std::unique_ptr<rtc::SSLFingerprint> fingerprint;
192 if (cert) {
Steve Anton4905edb2018-10-15 19:27:44 -0700193 fingerprint = rtc::SSLFingerprint::CreateFromCertificate(*cert);
Zhi Huange818b6e2018-02-22 15:26:27 -0800194 }
195
196 cricket::TransportDescription transport_desc(std::vector<std::string>(),
197 ufrag, pwd, ice_mode,
198 conn_role, fingerprint.get());
199 description->AddTransportInfo(cricket::TransportInfo(mid, transport_desc));
200 }
201
202 cricket::IceConfig CreateIceConfig(
203 int receiving_timeout,
204 cricket::ContinualGatheringPolicy continual_gathering_policy) {
205 cricket::IceConfig config;
206 config.receiving_timeout = receiving_timeout;
207 config.continual_gathering_policy = continual_gathering_policy;
208 return config;
209 }
210
211 Candidate CreateCandidate(const std::string& transport_name, int component) {
212 Candidate c;
213 c.set_transport_name(transport_name);
214 c.set_address(rtc::SocketAddress("192.168.1.1", 8000));
215 c.set_component(component);
216 c.set_protocol(cricket::UDP_PROTOCOL_NAME);
217 c.set_priority(1);
218 return c;
219 }
220
221 void CreateLocalDescriptionAndCompleteConnectionOnNetworkThread() {
222 if (!network_thread_->IsCurrent()) {
223 network_thread_->Invoke<void>(RTC_FROM_HERE, [&] {
224 CreateLocalDescriptionAndCompleteConnectionOnNetworkThread();
225 });
226 return;
227 }
228
229 auto description = CreateSessionDescriptionWithBundleGroup();
230 EXPECT_TRUE(transport_controller_
231 ->SetLocalDescription(SdpType::kOffer, description.get())
232 .ok());
233
234 transport_controller_->MaybeStartGathering();
235 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
236 transport_controller_->GetDtlsTransport(kAudioMid1));
237 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
238 transport_controller_->GetDtlsTransport(kVideoMid1));
239 fake_audio_dtls->fake_ice_transport()->SignalCandidateGathered(
240 fake_audio_dtls->fake_ice_transport(),
241 CreateCandidate(kAudioMid1, /*component=*/1));
242 fake_video_dtls->fake_ice_transport()->SignalCandidateGathered(
243 fake_video_dtls->fake_ice_transport(),
244 CreateCandidate(kVideoMid1, /*component=*/1));
245 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
246 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
247 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(2);
248 fake_video_dtls->fake_ice_transport()->SetConnectionCount(2);
249 fake_audio_dtls->SetReceiving(true);
250 fake_video_dtls->SetReceiving(true);
251 fake_audio_dtls->SetWritable(true);
252 fake_video_dtls->SetWritable(true);
253 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
254 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
255 }
256
257 protected:
Alex Loiko9289eda2018-11-23 16:18:59 +0000258 void OnConnectionState(cricket::IceConnectionState state) {
Zhi Huange818b6e2018-02-22 15:26:27 -0800259 if (!signaling_thread_->IsCurrent()) {
260 signaled_on_non_signaling_thread_ = true;
261 }
262 connection_state_ = state;
263 ++connection_state_signal_count_;
264 }
265
Alex Loiko9289eda2018-11-23 16:18:59 +0000266 void OnStandardizedIceConnectionState(
267 PeerConnectionInterface::IceConnectionState state) {
268 if (!signaling_thread_->IsCurrent()) {
269 signaled_on_non_signaling_thread_ = true;
270 }
271 ice_connection_state_ = state;
272 ++ice_connection_state_signal_count_;
273 }
274
Jonas Olsson635474e2018-10-18 15:58:17 +0200275 void OnCombinedConnectionState(
276 PeerConnectionInterface::PeerConnectionState state) {
Jonas Olsson6a8727b2018-12-07 13:11:44 +0100277 RTC_LOG(LS_INFO) << "OnCombinedConnectionState: "
278 << static_cast<int>(state);
Jonas Olsson635474e2018-10-18 15:58:17 +0200279 if (!signaling_thread_->IsCurrent()) {
280 signaled_on_non_signaling_thread_ = true;
281 }
282 combined_connection_state_ = state;
283 ++combined_connection_state_signal_count_;
284 }
285
Zhi Huange818b6e2018-02-22 15:26:27 -0800286 void OnGatheringState(cricket::IceGatheringState state) {
287 if (!signaling_thread_->IsCurrent()) {
288 signaled_on_non_signaling_thread_ = true;
289 }
290 gathering_state_ = state;
291 ++gathering_state_signal_count_;
292 }
293
294 void OnCandidatesGathered(const std::string& transport_name,
295 const Candidates& candidates) {
296 if (!signaling_thread_->IsCurrent()) {
297 signaled_on_non_signaling_thread_ = true;
298 }
299 candidates_[transport_name].insert(candidates_[transport_name].end(),
300 candidates.begin(), candidates.end());
301 ++candidates_signal_count_;
302 }
303
Zhi Huang365381f2018-04-13 16:44:34 -0700304 // JsepTransportController::Observer overrides.
Piotr (Peter) Slatalacc8e8bb2018-11-15 08:26:19 -0800305 bool OnTransportChanged(const std::string& mid,
306 RtpTransportInternal* rtp_transport,
307 cricket::DtlsTransportInternal* dtls_transport,
308 MediaTransportInterface* media_transport) override {
Taylor Brandstettercbaa2542018-04-16 16:42:14 -0700309 changed_rtp_transport_by_mid_[mid] = rtp_transport;
Zhi Huange818b6e2018-02-22 15:26:27 -0800310 changed_dtls_transport_by_mid_[mid] = dtls_transport;
Piotr (Peter) Slatalacc8e8bb2018-11-15 08:26:19 -0800311 changed_media_transport_by_mid_[mid] = media_transport;
Taylor Brandstettercbaa2542018-04-16 16:42:14 -0700312 return true;
Zhi Huange818b6e2018-02-22 15:26:27 -0800313 }
314
315 // Information received from signals from transport controller.
Alex Loiko9289eda2018-11-23 16:18:59 +0000316 cricket::IceConnectionState connection_state_ =
317 cricket::kIceConnectionConnecting;
318 PeerConnectionInterface::IceConnectionState ice_connection_state_ =
Jonas Olsson635474e2018-10-18 15:58:17 +0200319 PeerConnectionInterface::kIceConnectionNew;
320 PeerConnectionInterface::PeerConnectionState combined_connection_state_ =
321 PeerConnectionInterface::PeerConnectionState::kNew;
Zhi Huange818b6e2018-02-22 15:26:27 -0800322 bool receiving_ = false;
323 cricket::IceGatheringState gathering_state_ = cricket::kIceGatheringNew;
324 // transport_name => candidates
325 std::map<std::string, Candidates> candidates_;
326 // Counts of each signal emitted.
327 int connection_state_signal_count_ = 0;
Alex Loiko9289eda2018-11-23 16:18:59 +0000328 int ice_connection_state_signal_count_ = 0;
Jonas Olsson635474e2018-10-18 15:58:17 +0200329 int combined_connection_state_signal_count_ = 0;
Zhi Huange818b6e2018-02-22 15:26:27 -0800330 int receiving_signal_count_ = 0;
331 int gathering_state_signal_count_ = 0;
332 int candidates_signal_count_ = 0;
333
334 // |network_thread_| should be destroyed after |transport_controller_|
335 std::unique_ptr<rtc::Thread> network_thread_;
Zhi Huange818b6e2018-02-22 15:26:27 -0800336 std::unique_ptr<FakeTransportFactory> fake_transport_factory_;
337 rtc::Thread* const signaling_thread_ = nullptr;
338 bool signaled_on_non_signaling_thread_ = false;
339 // Used to verify the SignalRtpTransportChanged/SignalDtlsTransportChanged are
340 // signaled correctly.
341 std::map<std::string, RtpTransportInternal*> changed_rtp_transport_by_mid_;
342 std::map<std::string, cricket::DtlsTransportInternal*>
343 changed_dtls_transport_by_mid_;
Piotr (Peter) Slatalacc8e8bb2018-11-15 08:26:19 -0800344 std::map<std::string, MediaTransportInterface*>
345 changed_media_transport_by_mid_;
346
347 // Transport controller needs to be destroyed first, because it may issue
348 // callbacks that modify the changed_*_by_mid in the destructor.
349 std::unique_ptr<JsepTransportController> transport_controller_;
Zhi Huange818b6e2018-02-22 15:26:27 -0800350};
351
352TEST_F(JsepTransportControllerTest, GetRtpTransport) {
353 CreateJsepTransportController(JsepTransportController::Config());
354 auto description = CreateSessionDescriptionWithoutBundle();
355 EXPECT_TRUE(transport_controller_
356 ->SetLocalDescription(SdpType::kOffer, description.get())
357 .ok());
358 auto audio_rtp_transport = transport_controller_->GetRtpTransport(kAudioMid1);
359 auto video_rtp_transport = transport_controller_->GetRtpTransport(kVideoMid1);
360 EXPECT_NE(nullptr, audio_rtp_transport);
361 EXPECT_NE(nullptr, video_rtp_transport);
362 EXPECT_NE(audio_rtp_transport, video_rtp_transport);
363 // Return nullptr for non-existing ones.
364 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kAudioMid2));
365}
366
367TEST_F(JsepTransportControllerTest, GetDtlsTransport) {
368 JsepTransportController::Config config;
369 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
370 CreateJsepTransportController(config);
371 auto description = CreateSessionDescriptionWithoutBundle();
372 EXPECT_TRUE(transport_controller_
373 ->SetLocalDescription(SdpType::kOffer, description.get())
374 .ok());
375 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kAudioMid1));
376 EXPECT_NE(nullptr, transport_controller_->GetRtcpDtlsTransport(kAudioMid1));
Harald Alvestrandad88c882018-11-28 16:47:46 +0100377 EXPECT_NE(nullptr,
378 transport_controller_->LookupDtlsTransportByMid(kAudioMid1));
Zhi Huange818b6e2018-02-22 15:26:27 -0800379 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kVideoMid1));
380 EXPECT_NE(nullptr, transport_controller_->GetRtcpDtlsTransport(kVideoMid1));
Harald Alvestrandad88c882018-11-28 16:47:46 +0100381 EXPECT_NE(nullptr,
382 transport_controller_->LookupDtlsTransportByMid(kVideoMid1));
383 // Lookup for all MIDs should return different transports (no bundle)
384 EXPECT_NE(transport_controller_->LookupDtlsTransportByMid(kAudioMid1),
385 transport_controller_->LookupDtlsTransportByMid(kVideoMid1));
Zhi Huange818b6e2018-02-22 15:26:27 -0800386 // Return nullptr for non-existing ones.
387 EXPECT_EQ(nullptr, transport_controller_->GetDtlsTransport(kVideoMid2));
388 EXPECT_EQ(nullptr, transport_controller_->GetRtcpDtlsTransport(kVideoMid2));
Harald Alvestrandad88c882018-11-28 16:47:46 +0100389 EXPECT_EQ(nullptr,
390 transport_controller_->LookupDtlsTransportByMid(kVideoMid2));
Harald Alvestrand628f37a2018-12-06 10:55:20 +0100391 // Take a pointer to a transport, shut down the transport controller,
392 // and verify that the resulting container is empty.
393 auto dtls_transport =
394 transport_controller_->LookupDtlsTransportByMid(kVideoMid1);
395 webrtc::DtlsTransport* my_transport =
396 static_cast<DtlsTransport*>(dtls_transport.get());
397 EXPECT_NE(nullptr, my_transport->internal());
398 transport_controller_.reset();
399 EXPECT_EQ(nullptr, my_transport->internal());
Zhi Huange818b6e2018-02-22 15:26:27 -0800400}
401
Zhi Huange830e682018-03-30 10:48:35 -0700402TEST_F(JsepTransportControllerTest, GetDtlsTransportWithRtcpMux) {
403 JsepTransportController::Config config;
404 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
405 CreateJsepTransportController(config);
406 auto description = CreateSessionDescriptionWithoutBundle();
407 EXPECT_TRUE(transport_controller_
408 ->SetLocalDescription(SdpType::kOffer, description.get())
409 .ok());
410 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kAudioMid1));
411 EXPECT_EQ(nullptr, transport_controller_->GetRtcpDtlsTransport(kAudioMid1));
412 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kVideoMid1));
413 EXPECT_EQ(nullptr, transport_controller_->GetRtcpDtlsTransport(kVideoMid1));
Anton Sukhanov7940da02018-10-10 10:34:49 -0700414 EXPECT_EQ(nullptr, transport_controller_->GetMediaTransport(kAudioMid1));
415}
416
417TEST_F(JsepTransportControllerTest, GetMediaTransportInCaller) {
418 FakeMediaTransportFactory fake_media_transport_factory;
419 JsepTransportController::Config config;
420
421 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
422 config.media_transport_factory = &fake_media_transport_factory;
423 CreateJsepTransportController(config);
424 auto description = CreateSessionDescriptionWithoutBundle();
Piotr (Peter) Slatala9f956252018-10-31 08:25:26 -0700425 AddCryptoSettings(description.get());
426
Anton Sukhanov7940da02018-10-10 10:34:49 -0700427 EXPECT_TRUE(transport_controller_
428 ->SetLocalDescription(SdpType::kOffer, description.get())
429 .ok());
430
431 FakeMediaTransport* media_transport = static_cast<FakeMediaTransport*>(
432 transport_controller_->GetMediaTransport(kAudioMid1));
433
434 ASSERT_NE(nullptr, media_transport);
435
436 // After SetLocalDescription, media transport should be created as caller.
437 EXPECT_TRUE(media_transport->is_caller());
Piotr (Peter) Slatala9f956252018-10-31 08:25:26 -0700438 EXPECT_TRUE(media_transport->pre_shared_key().has_value());
Anton Sukhanov7940da02018-10-10 10:34:49 -0700439
440 // Return nullptr for non-existing mids.
441 EXPECT_EQ(nullptr, transport_controller_->GetMediaTransport(kVideoMid2));
442}
443
444TEST_F(JsepTransportControllerTest, GetMediaTransportInCallee) {
445 FakeMediaTransportFactory fake_media_transport_factory;
446 JsepTransportController::Config config;
447
448 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
449 config.media_transport_factory = &fake_media_transport_factory;
450 CreateJsepTransportController(config);
451 auto description = CreateSessionDescriptionWithoutBundle();
Piotr (Peter) Slatala9f956252018-10-31 08:25:26 -0700452 AddCryptoSettings(description.get());
Anton Sukhanov7940da02018-10-10 10:34:49 -0700453 EXPECT_TRUE(transport_controller_
454 ->SetRemoteDescription(SdpType::kOffer, description.get())
455 .ok());
456
457 FakeMediaTransport* media_transport = static_cast<FakeMediaTransport*>(
458 transport_controller_->GetMediaTransport(kAudioMid1));
459
460 ASSERT_NE(nullptr, media_transport);
461
462 // After SetRemoteDescription, media transport should be created as callee.
463 EXPECT_FALSE(media_transport->is_caller());
Piotr (Peter) Slatala9f956252018-10-31 08:25:26 -0700464 EXPECT_TRUE(media_transport->pre_shared_key().has_value());
Anton Sukhanov7940da02018-10-10 10:34:49 -0700465
466 // Return nullptr for non-existing mids.
467 EXPECT_EQ(nullptr, transport_controller_->GetMediaTransport(kVideoMid2));
Zhi Huange830e682018-03-30 10:48:35 -0700468}
469
Piotr (Peter) Slatala9f956252018-10-31 08:25:26 -0700470TEST_F(JsepTransportControllerTest, GetMediaTransportIsNotSetIfNoSdes) {
471 FakeMediaTransportFactory fake_media_transport_factory;
472 JsepTransportController::Config config;
473
474 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
475 config.media_transport_factory = &fake_media_transport_factory;
476 CreateJsepTransportController(config);
477 auto description = CreateSessionDescriptionWithoutBundle();
478 EXPECT_TRUE(transport_controller_
479 ->SetRemoteDescription(SdpType::kOffer, description.get())
480 .ok());
481
482 EXPECT_EQ(nullptr, transport_controller_->GetMediaTransport(kAudioMid1));
483
484 // Even if we set local description with crypto now (after the remote offer
485 // was set), media transport won't be provided.
486 auto description2 = CreateSessionDescriptionWithoutBundle();
487 AddCryptoSettings(description2.get());
488 EXPECT_TRUE(transport_controller_
489 ->SetLocalDescription(SdpType::kAnswer, description2.get())
490 .ok());
491
492 EXPECT_EQ(nullptr, transport_controller_->GetMediaTransport(kAudioMid1));
493}
494
495TEST_F(JsepTransportControllerTest,
496 AfterSettingAnswerTheSameMediaTransportIsReturned) {
497 FakeMediaTransportFactory fake_media_transport_factory;
498 JsepTransportController::Config config;
499
500 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
501 config.media_transport_factory = &fake_media_transport_factory;
502 CreateJsepTransportController(config);
503 auto description = CreateSessionDescriptionWithoutBundle();
504 AddCryptoSettings(description.get());
505 EXPECT_TRUE(transport_controller_
506 ->SetRemoteDescription(SdpType::kOffer, description.get())
507 .ok());
508
509 FakeMediaTransport* media_transport = static_cast<FakeMediaTransport*>(
510 transport_controller_->GetMediaTransport(kAudioMid1));
511 EXPECT_NE(nullptr, media_transport);
512 EXPECT_TRUE(media_transport->pre_shared_key().has_value());
513
514 // Even if we set local description with crypto now (after the remote offer
515 // was set), media transport won't be provided.
516 auto description2 = CreateSessionDescriptionWithoutBundle();
517 AddCryptoSettings(description2.get());
518
519 RTCError result = transport_controller_->SetLocalDescription(
520 SdpType::kAnswer, description2.get());
521 EXPECT_TRUE(result.ok()) << result.message();
522
523 // Media transport did not change.
524 EXPECT_EQ(media_transport,
525 transport_controller_->GetMediaTransport(kAudioMid1));
526}
527
Zhi Huange818b6e2018-02-22 15:26:27 -0800528TEST_F(JsepTransportControllerTest, SetIceConfig) {
529 CreateJsepTransportController(JsepTransportController::Config());
530 auto description = CreateSessionDescriptionWithoutBundle();
531 EXPECT_TRUE(transport_controller_
532 ->SetLocalDescription(SdpType::kOffer, description.get())
533 .ok());
534
535 transport_controller_->SetIceConfig(
536 CreateIceConfig(kTimeout, cricket::GATHER_CONTINUALLY));
537 FakeDtlsTransport* fake_audio_dtls = static_cast<FakeDtlsTransport*>(
538 transport_controller_->GetDtlsTransport(kAudioMid1));
539 ASSERT_NE(nullptr, fake_audio_dtls);
540 EXPECT_EQ(kTimeout,
541 fake_audio_dtls->fake_ice_transport()->receiving_timeout());
542 EXPECT_TRUE(fake_audio_dtls->fake_ice_transport()->gather_continually());
543
544 // Test that value stored in controller is applied to new transports.
545 AddAudioSection(description.get(), kAudioMid2, kIceUfrag1, kIcePwd1,
546 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
547 nullptr);
548
549 EXPECT_TRUE(transport_controller_
550 ->SetLocalDescription(SdpType::kOffer, description.get())
551 .ok());
552 fake_audio_dtls = static_cast<FakeDtlsTransport*>(
553 transport_controller_->GetDtlsTransport(kAudioMid2));
554 ASSERT_NE(nullptr, fake_audio_dtls);
555 EXPECT_EQ(kTimeout,
556 fake_audio_dtls->fake_ice_transport()->receiving_timeout());
557 EXPECT_TRUE(fake_audio_dtls->fake_ice_transport()->gather_continually());
558}
559
560// Tests the getter and setter of the ICE restart flag.
561TEST_F(JsepTransportControllerTest, NeedIceRestart) {
562 CreateJsepTransportController(JsepTransportController::Config());
563 auto description = CreateSessionDescriptionWithoutBundle();
564 EXPECT_TRUE(transport_controller_
565 ->SetLocalDescription(SdpType::kOffer, description.get())
566 .ok());
567 EXPECT_TRUE(transport_controller_
568 ->SetRemoteDescription(SdpType::kAnswer, description.get())
569 .ok());
570
571 // Initially NeedsIceRestart should return false.
572 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kAudioMid1));
573 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kVideoMid1));
574 // Set the needs-ice-restart flag and verify NeedsIceRestart starts returning
575 // true.
576 transport_controller_->SetNeedsIceRestartFlag();
577 EXPECT_TRUE(transport_controller_->NeedsIceRestart(kAudioMid1));
578 EXPECT_TRUE(transport_controller_->NeedsIceRestart(kVideoMid1));
579 // For a nonexistent transport, false should be returned.
580 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kVideoMid2));
581
582 // Reset the ice_ufrag/ice_pwd for audio.
583 auto audio_transport_info = description->GetTransportInfoByName(kAudioMid1);
584 audio_transport_info->description.ice_ufrag = kIceUfrag2;
585 audio_transport_info->description.ice_pwd = kIcePwd2;
586 EXPECT_TRUE(transport_controller_
587 ->SetLocalDescription(SdpType::kOffer, description.get())
588 .ok());
589 // Because the ICE is only restarted for audio, NeedsIceRestart is expected to
590 // return false for audio and true for video.
591 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kAudioMid1));
592 EXPECT_TRUE(transport_controller_->NeedsIceRestart(kVideoMid1));
593}
594
595TEST_F(JsepTransportControllerTest, MaybeStartGathering) {
596 CreateJsepTransportController(JsepTransportController::Config());
597 auto description = CreateSessionDescriptionWithBundleGroup();
598 EXPECT_TRUE(transport_controller_
599 ->SetLocalDescription(SdpType::kOffer, description.get())
600 .ok());
601 // After setting the local description, we should be able to start gathering
602 // candidates.
603 transport_controller_->MaybeStartGathering();
604 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
605 EXPECT_EQ(1, gathering_state_signal_count_);
606}
607
608TEST_F(JsepTransportControllerTest, AddRemoveRemoteCandidates) {
609 CreateJsepTransportController(JsepTransportController::Config());
610 auto description = CreateSessionDescriptionWithoutBundle();
611 transport_controller_->SetLocalDescription(SdpType::kOffer,
612 description.get());
613 transport_controller_->SetRemoteDescription(SdpType::kAnswer,
614 description.get());
615 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
616 transport_controller_->GetDtlsTransport(kAudioMid1));
617 ASSERT_NE(nullptr, fake_audio_dtls);
618 Candidates candidates;
619 candidates.push_back(
620 CreateCandidate(kAudioMid1, cricket::ICE_CANDIDATE_COMPONENT_RTP));
621 EXPECT_TRUE(
622 transport_controller_->AddRemoteCandidates(kAudioMid1, candidates).ok());
623 EXPECT_EQ(1U,
624 fake_audio_dtls->fake_ice_transport()->remote_candidates().size());
625
626 EXPECT_TRUE(transport_controller_->RemoveRemoteCandidates(candidates).ok());
627 EXPECT_EQ(0U,
628 fake_audio_dtls->fake_ice_transport()->remote_candidates().size());
629}
630
631TEST_F(JsepTransportControllerTest, SetAndGetLocalCertificate) {
632 CreateJsepTransportController(JsepTransportController::Config());
633
634 rtc::scoped_refptr<rtc::RTCCertificate> certificate1 =
635 rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
636 rtc::SSLIdentity::Generate("session1", rtc::KT_DEFAULT)));
637 rtc::scoped_refptr<rtc::RTCCertificate> returned_certificate;
638
Karl Wiberg918f50c2018-07-05 11:40:33 +0200639 auto description = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800640 AddAudioSection(description.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
641 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
642 certificate1);
643
644 // Apply the local certificate.
645 EXPECT_TRUE(transport_controller_->SetLocalCertificate(certificate1));
646 // Apply the local description.
647 EXPECT_TRUE(transport_controller_
648 ->SetLocalDescription(SdpType::kOffer, description.get())
649 .ok());
650 returned_certificate = transport_controller_->GetLocalCertificate(kAudioMid1);
651 EXPECT_TRUE(returned_certificate);
652 EXPECT_EQ(certificate1->identity()->certificate().ToPEMString(),
653 returned_certificate->identity()->certificate().ToPEMString());
654
655 // Should fail if called for a nonexistant transport.
656 EXPECT_EQ(nullptr, transport_controller_->GetLocalCertificate(kVideoMid1));
657
658 // Shouldn't be able to change the identity once set.
659 rtc::scoped_refptr<rtc::RTCCertificate> certificate2 =
660 rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
661 rtc::SSLIdentity::Generate("session2", rtc::KT_DEFAULT)));
662 EXPECT_FALSE(transport_controller_->SetLocalCertificate(certificate2));
663}
664
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800665TEST_F(JsepTransportControllerTest, GetRemoteSSLCertChain) {
Zhi Huange818b6e2018-02-22 15:26:27 -0800666 CreateJsepTransportController(JsepTransportController::Config());
667 auto description = CreateSessionDescriptionWithBundleGroup();
668 EXPECT_TRUE(transport_controller_
669 ->SetLocalDescription(SdpType::kOffer, description.get())
670 .ok());
671 rtc::FakeSSLCertificate fake_certificate("fake_data");
672
673 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
674 transport_controller_->GetDtlsTransport(kAudioMid1));
675 fake_audio_dtls->SetRemoteSSLCertificate(&fake_certificate);
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800676 std::unique_ptr<rtc::SSLCertChain> returned_cert_chain =
677 transport_controller_->GetRemoteSSLCertChain(kAudioMid1);
678 ASSERT_TRUE(returned_cert_chain);
679 ASSERT_EQ(1u, returned_cert_chain->GetSize());
Zhi Huange818b6e2018-02-22 15:26:27 -0800680 EXPECT_EQ(fake_certificate.ToPEMString(),
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800681 returned_cert_chain->Get(0).ToPEMString());
Zhi Huange818b6e2018-02-22 15:26:27 -0800682
683 // Should fail if called for a nonexistant transport.
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800684 EXPECT_FALSE(transport_controller_->GetRemoteSSLCertChain(kAudioMid2));
Zhi Huange818b6e2018-02-22 15:26:27 -0800685}
686
687TEST_F(JsepTransportControllerTest, GetDtlsRole) {
688 CreateJsepTransportController(JsepTransportController::Config());
689 auto offer_certificate =
690 rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
691 rtc::SSLIdentity::Generate("offer", rtc::KT_DEFAULT)));
692 auto answer_certificate =
693 rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
694 rtc::SSLIdentity::Generate("answer", rtc::KT_DEFAULT)));
695 transport_controller_->SetLocalCertificate(offer_certificate);
696
Karl Wiberg918f50c2018-07-05 11:40:33 +0200697 auto offer_desc = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800698 AddAudioSection(offer_desc.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
699 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
700 offer_certificate);
Karl Wiberg918f50c2018-07-05 11:40:33 +0200701 auto answer_desc = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800702 AddAudioSection(answer_desc.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
703 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
704 answer_certificate);
705
706 EXPECT_TRUE(transport_controller_
707 ->SetLocalDescription(SdpType::kOffer, offer_desc.get())
708 .ok());
709
Danil Chapovalov66cadcc2018-06-19 16:47:43 +0200710 absl::optional<rtc::SSLRole> role =
Zhi Huange818b6e2018-02-22 15:26:27 -0800711 transport_controller_->GetDtlsRole(kAudioMid1);
712 // The DTLS role is not decided yet.
713 EXPECT_FALSE(role);
714 EXPECT_TRUE(transport_controller_
715 ->SetRemoteDescription(SdpType::kAnswer, answer_desc.get())
716 .ok());
717 role = transport_controller_->GetDtlsRole(kAudioMid1);
718
719 ASSERT_TRUE(role);
720 EXPECT_EQ(rtc::SSL_CLIENT, *role);
721}
722
723TEST_F(JsepTransportControllerTest, GetStats) {
724 CreateJsepTransportController(JsepTransportController::Config());
725 auto description = CreateSessionDescriptionWithBundleGroup();
726 EXPECT_TRUE(transport_controller_
727 ->SetLocalDescription(SdpType::kOffer, description.get())
728 .ok());
729
730 cricket::TransportStats stats;
731 EXPECT_TRUE(transport_controller_->GetStats(kAudioMid1, &stats));
732 EXPECT_EQ(kAudioMid1, stats.transport_name);
733 EXPECT_EQ(1u, stats.channel_stats.size());
734 // Return false for non-existing transport.
735 EXPECT_FALSE(transport_controller_->GetStats(kAudioMid2, &stats));
736}
737
738TEST_F(JsepTransportControllerTest, SignalConnectionStateFailed) {
739 CreateJsepTransportController(JsepTransportController::Config());
740 auto description = CreateSessionDescriptionWithoutBundle();
741 EXPECT_TRUE(transport_controller_
742 ->SetLocalDescription(SdpType::kOffer, description.get())
743 .ok());
744
745 auto fake_ice = static_cast<cricket::FakeIceTransport*>(
746 transport_controller_->GetDtlsTransport(kAudioMid1)->ice_transport());
747 fake_ice->SetCandidatesGatheringComplete();
748 fake_ice->SetConnectionCount(1);
749 // The connection stats will be failed if there is no active connection.
750 fake_ice->SetConnectionCount(0);
Alex Loiko9289eda2018-11-23 16:18:59 +0000751 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
Jonas Olsson1e87b4f2018-11-22 16:50:37 +0100752 EXPECT_EQ(1, connection_state_signal_count_);
Alex Loiko9289eda2018-11-23 16:18:59 +0000753 EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionFailed,
754 ice_connection_state_, kTimeout);
755 EXPECT_EQ(1, ice_connection_state_signal_count_);
Jonas Olssond8aa9f92018-11-09 10:51:09 +0100756 EXPECT_EQ_WAIT(PeerConnectionInterface::PeerConnectionState::kFailed,
757 combined_connection_state_, kTimeout);
Jonas Olsson635474e2018-10-18 15:58:17 +0200758 EXPECT_EQ(1, combined_connection_state_signal_count_);
Zhi Huange818b6e2018-02-22 15:26:27 -0800759}
760
Piotr (Peter) Slatala4eb41122018-11-01 07:26:03 -0700761TEST_F(JsepTransportControllerTest,
762 SignalConnectionStateConnectedNoMediaTransport) {
Zhi Huange818b6e2018-02-22 15:26:27 -0800763 CreateJsepTransportController(JsepTransportController::Config());
764 auto description = CreateSessionDescriptionWithoutBundle();
765 EXPECT_TRUE(transport_controller_
766 ->SetLocalDescription(SdpType::kOffer, description.get())
767 .ok());
768
769 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
770 transport_controller_->GetDtlsTransport(kAudioMid1));
771 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
772 transport_controller_->GetDtlsTransport(kVideoMid1));
773
774 // First, have one transport connect, and another fail, to ensure that
775 // the first transport connecting didn't trigger a "connected" state signal.
776 // We should only get a signal when all are connected.
777 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
778 fake_audio_dtls->SetWritable(true);
779 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
780 // Decrease the number of the connection to trigger the signal.
781 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
782 fake_video_dtls->fake_ice_transport()->SetConnectionCount(0);
783 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
784
Alex Loiko9289eda2018-11-23 16:18:59 +0000785 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
Jonas Olsson1e87b4f2018-11-22 16:50:37 +0100786 EXPECT_EQ(1, connection_state_signal_count_);
Alex Loiko9289eda2018-11-23 16:18:59 +0000787 EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionFailed,
788 ice_connection_state_, kTimeout);
Jonas Olsson6a8727b2018-12-07 13:11:44 +0100789 EXPECT_EQ(2, ice_connection_state_signal_count_);
Jonas Olssond8aa9f92018-11-09 10:51:09 +0100790 EXPECT_EQ_WAIT(PeerConnectionInterface::PeerConnectionState::kFailed,
791 combined_connection_state_, kTimeout);
Jonas Olsson6a8727b2018-12-07 13:11:44 +0100792 EXPECT_EQ(2, combined_connection_state_signal_count_);
Zhi Huange818b6e2018-02-22 15:26:27 -0800793
Jonas Olsson635474e2018-10-18 15:58:17 +0200794 fake_audio_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
795 fake_video_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
Zhi Huange818b6e2018-02-22 15:26:27 -0800796 // Set the connection count to be 2 and the cricket::FakeIceTransport will set
797 // the transport state to be STATE_CONNECTING.
798 fake_video_dtls->fake_ice_transport()->SetConnectionCount(2);
799 fake_video_dtls->SetWritable(true);
Alex Loiko9289eda2018-11-23 16:18:59 +0000800 EXPECT_EQ_WAIT(cricket::kIceConnectionConnected, connection_state_, kTimeout);
Jonas Olsson1e87b4f2018-11-22 16:50:37 +0100801 EXPECT_EQ(2, connection_state_signal_count_);
Alex Loiko9289eda2018-11-23 16:18:59 +0000802 EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionConnected,
803 ice_connection_state_, kTimeout);
Jonas Olsson6a8727b2018-12-07 13:11:44 +0100804 EXPECT_EQ(3, ice_connection_state_signal_count_);
Jonas Olssond8aa9f92018-11-09 10:51:09 +0100805 EXPECT_EQ_WAIT(PeerConnectionInterface::PeerConnectionState::kConnected,
806 combined_connection_state_, kTimeout);
Jonas Olsson6a8727b2018-12-07 13:11:44 +0100807 EXPECT_EQ(3, combined_connection_state_signal_count_);
Zhi Huange818b6e2018-02-22 15:26:27 -0800808}
809
Piotr (Peter) Slatala4eb41122018-11-01 07:26:03 -0700810TEST_F(JsepTransportControllerTest,
811 SignalConnectionStateConnectedWithMediaTransport) {
812 FakeMediaTransportFactory fake_media_transport_factory;
813 JsepTransportController::Config config;
814 config.media_transport_factory = &fake_media_transport_factory;
815 CreateJsepTransportController(config);
816 auto description = CreateSessionDescriptionWithoutBundle();
817 AddCryptoSettings(description.get());
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 fake_audio_dtls->SetWritable(true);
827 fake_video_dtls->SetWritable(true);
828 // Decreasing connection count from 2 to 1 triggers connection state event.
829 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(2);
830 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
831 fake_video_dtls->fake_ice_transport()->SetConnectionCount(2);
832 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
833 fake_audio_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
834 fake_video_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
835
836 // Still not connected, because we are waiting for media transport.
Alex Loiko9289eda2018-11-23 16:18:59 +0000837 EXPECT_EQ_WAIT(cricket::kIceConnectionConnecting, connection_state_,
838 kTimeout);
Piotr (Peter) Slatala4eb41122018-11-01 07:26:03 -0700839
840 FakeMediaTransport* media_transport = static_cast<FakeMediaTransport*>(
841 transport_controller_->GetMediaTransport(kAudioMid1));
842
843 media_transport->SetState(webrtc::MediaTransportState::kWritable);
Alex Loiko9289eda2018-11-23 16:18:59 +0000844 EXPECT_EQ_WAIT(cricket::kIceConnectionConnecting, connection_state_,
845 kTimeout);
Piotr (Peter) Slatala4eb41122018-11-01 07:26:03 -0700846
847 // Still waiting for the second media transport.
848 media_transport = static_cast<FakeMediaTransport*>(
849 transport_controller_->GetMediaTransport(kVideoMid1));
850 media_transport->SetState(webrtc::MediaTransportState::kWritable);
851
Alex Loiko9289eda2018-11-23 16:18:59 +0000852 EXPECT_EQ_WAIT(cricket::kIceConnectionConnected, connection_state_, kTimeout);
Piotr (Peter) Slatala4eb41122018-11-01 07:26:03 -0700853}
854
855TEST_F(JsepTransportControllerTest,
856 SignalConnectionStateFailedWhenMediaTransportClosed) {
857 FakeMediaTransportFactory fake_media_transport_factory;
858 JsepTransportController::Config config;
859 config.media_transport_factory = &fake_media_transport_factory;
860 CreateJsepTransportController(config);
861 auto description = CreateSessionDescriptionWithoutBundle();
862 AddCryptoSettings(description.get());
863 EXPECT_TRUE(transport_controller_
864 ->SetLocalDescription(SdpType::kOffer, description.get())
865 .ok());
866
867 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
868 transport_controller_->GetDtlsTransport(kAudioMid1));
869 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
870 transport_controller_->GetDtlsTransport(kVideoMid1));
871 fake_audio_dtls->SetWritable(true);
872 fake_video_dtls->SetWritable(true);
873 // Decreasing connection count from 2 to 1 triggers connection state event.
874 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(2);
875 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
876 fake_video_dtls->fake_ice_transport()->SetConnectionCount(2);
877 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
878 fake_audio_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
879 fake_video_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
880
881 FakeMediaTransport* media_transport = static_cast<FakeMediaTransport*>(
882 transport_controller_->GetMediaTransport(kAudioMid1));
883
884 media_transport->SetState(webrtc::MediaTransportState::kWritable);
885
886 media_transport = static_cast<FakeMediaTransport*>(
887 transport_controller_->GetMediaTransport(kVideoMid1));
888
889 media_transport->SetState(webrtc::MediaTransportState::kWritable);
890
Alex Loiko9289eda2018-11-23 16:18:59 +0000891 EXPECT_EQ_WAIT(cricket::kIceConnectionConnected, connection_state_, kTimeout);
Piotr (Peter) Slatala4eb41122018-11-01 07:26:03 -0700892
893 media_transport->SetState(webrtc::MediaTransportState::kClosed);
Alex Loiko9289eda2018-11-23 16:18:59 +0000894 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
Piotr (Peter) Slatala4eb41122018-11-01 07:26:03 -0700895}
896
Zhi Huange818b6e2018-02-22 15:26:27 -0800897TEST_F(JsepTransportControllerTest, SignalConnectionStateComplete) {
898 CreateJsepTransportController(JsepTransportController::Config());
899 auto description = CreateSessionDescriptionWithoutBundle();
900 EXPECT_TRUE(transport_controller_
901 ->SetLocalDescription(SdpType::kOffer, description.get())
902 .ok());
903
904 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
905 transport_controller_->GetDtlsTransport(kAudioMid1));
906 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
907 transport_controller_->GetDtlsTransport(kVideoMid1));
908
909 // First, have one transport connect, and another fail, to ensure that
910 // the first transport connecting didn't trigger a "connected" state signal.
911 // We should only get a signal when all are connected.
Jonas Olsson6a8727b2018-12-07 13:11:44 +0100912 fake_audio_dtls->fake_ice_transport()->SetTransportState(
913 IceTransportState::kCompleted,
914 cricket::IceTransportState::STATE_COMPLETED);
Zhi Huange818b6e2018-02-22 15:26:27 -0800915 fake_audio_dtls->SetWritable(true);
916 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
Jonas Olsson6a8727b2018-12-07 13:11:44 +0100917
918 EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionChecking,
919 ice_connection_state_, kTimeout);
920 EXPECT_EQ(1, ice_connection_state_signal_count_);
921 EXPECT_EQ_WAIT(PeerConnectionInterface::PeerConnectionState::kConnecting,
922 combined_connection_state_, kTimeout);
923 EXPECT_EQ(1, combined_connection_state_signal_count_);
924
925 fake_video_dtls->fake_ice_transport()->SetTransportState(
926 IceTransportState::kFailed, cricket::IceTransportState::STATE_FAILED);
Zhi Huange818b6e2018-02-22 15:26:27 -0800927 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
928
Alex Loiko9289eda2018-11-23 16:18:59 +0000929 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
Jonas Olsson1e87b4f2018-11-22 16:50:37 +0100930 EXPECT_EQ(1, connection_state_signal_count_);
Alex Loiko9289eda2018-11-23 16:18:59 +0000931 EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionFailed,
932 ice_connection_state_, kTimeout);
Jonas Olsson6a8727b2018-12-07 13:11:44 +0100933 EXPECT_EQ(2, ice_connection_state_signal_count_);
Jonas Olssond8aa9f92018-11-09 10:51:09 +0100934 EXPECT_EQ_WAIT(PeerConnectionInterface::PeerConnectionState::kFailed,
935 combined_connection_state_, kTimeout);
Jonas Olsson6a8727b2018-12-07 13:11:44 +0100936 EXPECT_EQ(2, combined_connection_state_signal_count_);
Zhi Huange818b6e2018-02-22 15:26:27 -0800937
Jonas Olsson635474e2018-10-18 15:58:17 +0200938 fake_audio_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
939 fake_video_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
Zhi Huange818b6e2018-02-22 15:26:27 -0800940 // Set the connection count to be 1 and the cricket::FakeIceTransport will set
941 // the transport state to be STATE_COMPLETED.
Jonas Olsson6a8727b2018-12-07 13:11:44 +0100942 fake_video_dtls->fake_ice_transport()->SetTransportState(
943 IceTransportState::kCompleted,
944 cricket::IceTransportState::STATE_COMPLETED);
Zhi Huange818b6e2018-02-22 15:26:27 -0800945 fake_video_dtls->SetWritable(true);
Alex Loiko9289eda2018-11-23 16:18:59 +0000946 EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
Jonas Olsson7a6739e2019-01-15 16:31:55 +0100947 EXPECT_EQ(3, connection_state_signal_count_);
Alex Loiko9289eda2018-11-23 16:18:59 +0000948 EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionCompleted,
949 ice_connection_state_, kTimeout);
Jonas Olsson6a8727b2018-12-07 13:11:44 +0100950 EXPECT_EQ(3, ice_connection_state_signal_count_);
Jonas Olssond8aa9f92018-11-09 10:51:09 +0100951 EXPECT_EQ_WAIT(PeerConnectionInterface::PeerConnectionState::kConnected,
952 combined_connection_state_, kTimeout);
Jonas Olsson6a8727b2018-12-07 13:11:44 +0100953 EXPECT_EQ(3, combined_connection_state_signal_count_);
Zhi Huange818b6e2018-02-22 15:26:27 -0800954}
955
956TEST_F(JsepTransportControllerTest, SignalIceGatheringStateGathering) {
957 CreateJsepTransportController(JsepTransportController::Config());
958 auto description = CreateSessionDescriptionWithoutBundle();
959 EXPECT_TRUE(transport_controller_
960 ->SetLocalDescription(SdpType::kOffer, description.get())
961 .ok());
962
963 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
964 transport_controller_->GetDtlsTransport(kAudioMid1));
965 fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
966 // Should be in the gathering state as soon as any transport starts gathering.
967 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
968 EXPECT_EQ(1, gathering_state_signal_count_);
969}
970
971TEST_F(JsepTransportControllerTest, SignalIceGatheringStateComplete) {
972 CreateJsepTransportController(JsepTransportController::Config());
973 auto description = CreateSessionDescriptionWithoutBundle();
974 EXPECT_TRUE(transport_controller_
975 ->SetLocalDescription(SdpType::kOffer, description.get())
976 .ok());
977
978 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
979 transport_controller_->GetDtlsTransport(kAudioMid1));
980 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
981 transport_controller_->GetDtlsTransport(kVideoMid1));
982
983 fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
984 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
985 EXPECT_EQ(1, gathering_state_signal_count_);
986
987 // Have one transport finish gathering, to make sure gathering
988 // completion wasn't signalled if only one transport finished gathering.
989 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
990 EXPECT_EQ(1, gathering_state_signal_count_);
991
992 fake_video_dtls->fake_ice_transport()->MaybeStartGathering();
993 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
994 EXPECT_EQ(1, gathering_state_signal_count_);
995
996 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
997 EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
998 EXPECT_EQ(2, gathering_state_signal_count_);
999}
1000
1001// Test that when the last transport that hasn't finished connecting and/or
1002// gathering is destroyed, the aggregate state jumps to "completed". This can
1003// happen if, for example, we have an audio and video transport, the audio
1004// transport completes, then we start bundling video on the audio transport.
1005TEST_F(JsepTransportControllerTest,
1006 SignalingWhenLastIncompleteTransportDestroyed) {
1007 CreateJsepTransportController(JsepTransportController::Config());
1008 auto description = CreateSessionDescriptionWithBundleGroup();
1009 EXPECT_TRUE(transport_controller_
1010 ->SetLocalDescription(SdpType::kOffer, description.get())
1011 .ok());
1012
1013 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
1014 transport_controller_->GetDtlsTransport(kAudioMid1));
1015 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
1016 transport_controller_->GetDtlsTransport(kVideoMid1));
1017 EXPECT_NE(fake_audio_dtls, fake_video_dtls);
1018
1019 fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
1020 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
1021 EXPECT_EQ(1, gathering_state_signal_count_);
1022
1023 // Let the audio transport complete.
1024 fake_audio_dtls->SetWritable(true);
1025 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
1026 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
Jonas Olsson635474e2018-10-18 15:58:17 +02001027 fake_audio_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
Zhi Huange818b6e2018-02-22 15:26:27 -08001028 EXPECT_EQ(1, gathering_state_signal_count_);
1029
1030 // Set the remote description and enable the bundle.
1031 EXPECT_TRUE(transport_controller_
1032 ->SetRemoteDescription(SdpType::kAnswer, description.get())
1033 .ok());
1034 // The BUNDLE should be enabled, the incomplete video transport should be
1035 // deleted and the states shoud be updated.
1036 fake_video_dtls = static_cast<FakeDtlsTransport*>(
1037 transport_controller_->GetDtlsTransport(kVideoMid1));
1038 EXPECT_EQ(fake_audio_dtls, fake_video_dtls);
Alex Loiko9289eda2018-11-23 16:18:59 +00001039 EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
1040 EXPECT_EQ(PeerConnectionInterface::kIceConnectionCompleted,
1041 ice_connection_state_);
Jonas Olsson635474e2018-10-18 15:58:17 +02001042 EXPECT_EQ(PeerConnectionInterface::PeerConnectionState::kConnected,
1043 combined_connection_state_);
Zhi Huange818b6e2018-02-22 15:26:27 -08001044 EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
1045 EXPECT_EQ(2, gathering_state_signal_count_);
1046}
1047
1048TEST_F(JsepTransportControllerTest, SignalCandidatesGathered) {
1049 CreateJsepTransportController(JsepTransportController::Config());
1050 auto description = CreateSessionDescriptionWithBundleGroup();
1051 EXPECT_TRUE(transport_controller_
1052 ->SetLocalDescription(SdpType::kOffer, description.get())
1053 .ok());
1054 transport_controller_->MaybeStartGathering();
1055
1056 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
1057 transport_controller_->GetDtlsTransport(kAudioMid1));
1058 fake_audio_dtls->fake_ice_transport()->SignalCandidateGathered(
1059 fake_audio_dtls->fake_ice_transport(), CreateCandidate(kAudioMid1, 1));
1060 EXPECT_EQ_WAIT(1, candidates_signal_count_, kTimeout);
1061 EXPECT_EQ(1u, candidates_[kAudioMid1].size());
1062}
1063
1064TEST_F(JsepTransportControllerTest, IceSignalingOccursOnSignalingThread) {
1065 network_thread_ = rtc::Thread::CreateWithSocketServer();
1066 network_thread_->Start();
1067 CreateJsepTransportController(JsepTransportController::Config(),
1068 signaling_thread_, network_thread_.get(),
1069 /*PortAllocator=*/nullptr);
1070 CreateLocalDescriptionAndCompleteConnectionOnNetworkThread();
1071
1072 // connecting --> connected --> completed
Alex Loiko9289eda2018-11-23 16:18:59 +00001073 EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
Zhi Huange818b6e2018-02-22 15:26:27 -08001074 EXPECT_EQ(2, connection_state_signal_count_);
1075
1076 // new --> gathering --> complete
1077 EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
1078 EXPECT_EQ(2, gathering_state_signal_count_);
1079
1080 EXPECT_EQ_WAIT(1u, candidates_[kAudioMid1].size(), kTimeout);
1081 EXPECT_EQ_WAIT(1u, candidates_[kVideoMid1].size(), kTimeout);
1082 EXPECT_EQ(2, candidates_signal_count_);
1083
1084 EXPECT_TRUE(!signaled_on_non_signaling_thread_);
1085}
1086
1087// Older versions of Chrome expect the ICE role to be re-determined when an
1088// ICE restart occurs, and also don't perform conflict resolution correctly,
1089// so for now we can't safely stop doing this.
1090// See: https://bugs.chromium.org/p/chromium/issues/detail?id=628676
1091// TODO(deadbeef): Remove this when these old versions of Chrome reach a low
1092// enough population.
1093TEST_F(JsepTransportControllerTest, IceRoleRedeterminedOnIceRestartByDefault) {
1094 CreateJsepTransportController(JsepTransportController::Config());
1095 // Let the |transport_controller_| be the controlled side initially.
Karl Wiberg918f50c2018-07-05 11:40:33 +02001096 auto remote_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001097 AddAudioSection(remote_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1098 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1099 nullptr);
Karl Wiberg918f50c2018-07-05 11:40:33 +02001100 auto local_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001101 AddAudioSection(local_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1102 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1103 nullptr);
1104
1105 EXPECT_TRUE(transport_controller_
1106 ->SetRemoteDescription(SdpType::kOffer, remote_offer.get())
1107 .ok());
1108 EXPECT_TRUE(transport_controller_
1109 ->SetLocalDescription(SdpType::kAnswer, local_answer.get())
1110 .ok());
1111
1112 auto fake_dtls = static_cast<FakeDtlsTransport*>(
1113 transport_controller_->GetDtlsTransport(kAudioMid1));
1114 EXPECT_EQ(cricket::ICEROLE_CONTROLLED,
1115 fake_dtls->fake_ice_transport()->GetIceRole());
1116
1117 // New offer will trigger the ICE restart.
Karl Wiberg918f50c2018-07-05 11:40:33 +02001118 auto restart_local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001119 AddAudioSection(restart_local_offer.get(), kAudioMid1, kIceUfrag3, kIcePwd3,
1120 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1121 nullptr);
1122 EXPECT_TRUE(
1123 transport_controller_
1124 ->SetLocalDescription(SdpType::kOffer, restart_local_offer.get())
1125 .ok());
1126 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
1127 fake_dtls->fake_ice_transport()->GetIceRole());
1128}
1129
1130// Test that if the TransportController was created with the
1131// |redetermine_role_on_ice_restart| parameter set to false, the role is *not*
1132// redetermined on an ICE restart.
1133TEST_F(JsepTransportControllerTest, IceRoleNotRedetermined) {
1134 JsepTransportController::Config config;
1135 config.redetermine_role_on_ice_restart = false;
1136
1137 CreateJsepTransportController(config);
1138 // Let the |transport_controller_| be the controlled side initially.
Karl Wiberg918f50c2018-07-05 11:40:33 +02001139 auto remote_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001140 AddAudioSection(remote_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1141 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1142 nullptr);
Karl Wiberg918f50c2018-07-05 11:40:33 +02001143 auto local_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001144 AddAudioSection(local_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1145 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1146 nullptr);
1147
1148 EXPECT_TRUE(transport_controller_
1149 ->SetRemoteDescription(SdpType::kOffer, remote_offer.get())
1150 .ok());
1151 EXPECT_TRUE(transport_controller_
1152 ->SetLocalDescription(SdpType::kAnswer, local_answer.get())
1153 .ok());
1154
1155 auto fake_dtls = static_cast<FakeDtlsTransport*>(
1156 transport_controller_->GetDtlsTransport(kAudioMid1));
1157 EXPECT_EQ(cricket::ICEROLE_CONTROLLED,
1158 fake_dtls->fake_ice_transport()->GetIceRole());
1159
1160 // New offer will trigger the ICE restart.
Karl Wiberg918f50c2018-07-05 11:40:33 +02001161 auto restart_local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001162 AddAudioSection(restart_local_offer.get(), kAudioMid1, kIceUfrag3, kIcePwd3,
1163 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1164 nullptr);
1165 EXPECT_TRUE(
1166 transport_controller_
1167 ->SetLocalDescription(SdpType::kOffer, restart_local_offer.get())
1168 .ok());
1169 EXPECT_EQ(cricket::ICEROLE_CONTROLLED,
1170 fake_dtls->fake_ice_transport()->GetIceRole());
1171}
1172
1173// Tests ICE-Lite mode in remote answer.
1174TEST_F(JsepTransportControllerTest, SetIceRoleWhenIceLiteInRemoteAnswer) {
1175 CreateJsepTransportController(JsepTransportController::Config());
Karl Wiberg918f50c2018-07-05 11:40:33 +02001176 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001177 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1178 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1179 nullptr);
1180 EXPECT_TRUE(transport_controller_
1181 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1182 .ok());
1183 auto fake_dtls = static_cast<FakeDtlsTransport*>(
1184 transport_controller_->GetDtlsTransport(kAudioMid1));
1185 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
1186 fake_dtls->fake_ice_transport()->GetIceRole());
1187 EXPECT_EQ(cricket::ICEMODE_FULL,
1188 fake_dtls->fake_ice_transport()->remote_ice_mode());
1189
Karl Wiberg918f50c2018-07-05 11:40:33 +02001190 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001191 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1192 cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_PASSIVE,
1193 nullptr);
1194 EXPECT_TRUE(transport_controller_
1195 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1196 .ok());
1197 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
1198 fake_dtls->fake_ice_transport()->GetIceRole());
1199 EXPECT_EQ(cricket::ICEMODE_LITE,
1200 fake_dtls->fake_ice_transport()->remote_ice_mode());
1201}
1202
1203// Tests that the ICE role remains "controlling" if a subsequent offer that
1204// does an ICE restart is received from an ICE lite endpoint. Regression test
1205// for: https://crbug.com/710760
1206TEST_F(JsepTransportControllerTest,
1207 IceRoleIsControllingAfterIceRestartFromIceLiteEndpoint) {
1208 CreateJsepTransportController(JsepTransportController::Config());
Karl Wiberg918f50c2018-07-05 11:40:33 +02001209 auto remote_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001210 AddAudioSection(remote_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1211 cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_ACTPASS,
1212 nullptr);
Karl Wiberg918f50c2018-07-05 11:40:33 +02001213 auto local_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001214 AddAudioSection(local_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1215 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1216 nullptr);
1217 // Initial Offer/Answer exchange. If the remote offerer is ICE-Lite, then the
1218 // local side is the controlling.
1219 EXPECT_TRUE(transport_controller_
1220 ->SetRemoteDescription(SdpType::kOffer, remote_offer.get())
1221 .ok());
1222 EXPECT_TRUE(transport_controller_
1223 ->SetLocalDescription(SdpType::kAnswer, local_answer.get())
1224 .ok());
1225 auto fake_dtls = static_cast<FakeDtlsTransport*>(
1226 transport_controller_->GetDtlsTransport(kAudioMid1));
1227 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
1228 fake_dtls->fake_ice_transport()->GetIceRole());
1229
1230 // In the subsequence remote offer triggers an ICE restart.
Karl Wiberg918f50c2018-07-05 11:40:33 +02001231 auto remote_offer2 = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001232 AddAudioSection(remote_offer2.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1233 cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_ACTPASS,
1234 nullptr);
Karl Wiberg918f50c2018-07-05 11:40:33 +02001235 auto local_answer2 = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001236 AddAudioSection(local_answer2.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1237 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1238 nullptr);
1239 EXPECT_TRUE(transport_controller_
1240 ->SetRemoteDescription(SdpType::kOffer, remote_offer2.get())
1241 .ok());
1242 EXPECT_TRUE(transport_controller_
1243 ->SetLocalDescription(SdpType::kAnswer, local_answer2.get())
1244 .ok());
1245 fake_dtls = static_cast<FakeDtlsTransport*>(
1246 transport_controller_->GetDtlsTransport(kAudioMid1));
1247 // The local side is still the controlling role since the remote side is using
1248 // ICE-Lite.
1249 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
1250 fake_dtls->fake_ice_transport()->GetIceRole());
1251}
1252
1253// Tests that the SDP has more than one audio/video m= sections.
1254TEST_F(JsepTransportControllerTest, MultipleMediaSectionsOfSameTypeWithBundle) {
1255 CreateJsepTransportController(JsepTransportController::Config());
1256 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1257 bundle_group.AddContentName(kAudioMid1);
1258 bundle_group.AddContentName(kAudioMid2);
1259 bundle_group.AddContentName(kVideoMid1);
1260 bundle_group.AddContentName(kDataMid1);
1261
Karl Wiberg918f50c2018-07-05 11:40:33 +02001262 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001263 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1264 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1265 nullptr);
1266 AddAudioSection(local_offer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
1267 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1268 nullptr);
1269 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1270 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1271 nullptr);
1272 AddDataSection(local_offer.get(), kDataMid1,
1273 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
1274 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1275 nullptr);
1276
Karl Wiberg918f50c2018-07-05 11:40:33 +02001277 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001278 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1279 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1280 nullptr);
1281 AddAudioSection(remote_answer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
1282 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1283 nullptr);
1284 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1285 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1286 nullptr);
1287 AddDataSection(remote_answer.get(), kDataMid1,
1288 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
1289 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1290 nullptr);
1291
1292 local_offer->AddGroup(bundle_group);
1293 remote_answer->AddGroup(bundle_group);
1294
1295 EXPECT_TRUE(transport_controller_
1296 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1297 .ok());
1298 EXPECT_TRUE(transport_controller_
1299 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1300 .ok());
1301 // Verify that all the sections are bundled on kAudio1.
1302 auto transport1 = transport_controller_->GetRtpTransport(kAudioMid1);
1303 auto transport2 = transport_controller_->GetRtpTransport(kAudioMid2);
1304 auto transport3 = transport_controller_->GetRtpTransport(kVideoMid1);
1305 auto transport4 = transport_controller_->GetRtpTransport(kDataMid1);
1306 EXPECT_EQ(transport1, transport2);
1307 EXPECT_EQ(transport1, transport3);
1308 EXPECT_EQ(transport1, transport4);
1309
Harald Alvestrandad88c882018-11-28 16:47:46 +01001310 EXPECT_EQ(transport_controller_->LookupDtlsTransportByMid(kAudioMid1),
1311 transport_controller_->LookupDtlsTransportByMid(kVideoMid1));
1312
Zhi Huange818b6e2018-02-22 15:26:27 -08001313 // Verify the OnRtpTransport/DtlsTransportChanged signals are fired correctly.
1314 auto it = changed_rtp_transport_by_mid_.find(kAudioMid2);
1315 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1316 EXPECT_EQ(transport1, it->second);
1317 it = changed_rtp_transport_by_mid_.find(kAudioMid2);
1318 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1319 EXPECT_EQ(transport1, it->second);
1320 it = changed_rtp_transport_by_mid_.find(kVideoMid1);
1321 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1322 EXPECT_EQ(transport1, it->second);
1323 // Verify the DtlsTransport for the SCTP data channel is reset correctly.
1324 auto it2 = changed_dtls_transport_by_mid_.find(kDataMid1);
1325 ASSERT_TRUE(it2 != changed_dtls_transport_by_mid_.end());
1326 EXPECT_EQ(transport1->rtp_packet_transport(), it2->second);
1327}
1328
1329// Tests that only a subset of all the m= sections are bundled.
1330TEST_F(JsepTransportControllerTest, BundleSubsetOfMediaSections) {
1331 CreateJsepTransportController(JsepTransportController::Config());
1332 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1333 bundle_group.AddContentName(kAudioMid1);
1334 bundle_group.AddContentName(kVideoMid1);
1335
Karl Wiberg918f50c2018-07-05 11:40:33 +02001336 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001337 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1338 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1339 nullptr);
1340 AddAudioSection(local_offer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
1341 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1342 nullptr);
1343 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1344 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1345 nullptr);
1346
Karl Wiberg918f50c2018-07-05 11:40:33 +02001347 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001348 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1349 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1350 nullptr);
1351 AddAudioSection(remote_answer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
1352 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1353 nullptr);
1354 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1355 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1356 nullptr);
1357
1358 local_offer->AddGroup(bundle_group);
1359 remote_answer->AddGroup(bundle_group);
1360 EXPECT_TRUE(transport_controller_
1361 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1362 .ok());
1363 EXPECT_TRUE(transport_controller_
1364 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1365 .ok());
1366
1367 // Verifiy that only |kAudio1| and |kVideo1| are bundled.
1368 auto transport1 = transport_controller_->GetRtpTransport(kAudioMid1);
1369 auto transport2 = transport_controller_->GetRtpTransport(kAudioMid2);
1370 auto transport3 = transport_controller_->GetRtpTransport(kVideoMid1);
1371 EXPECT_NE(transport1, transport2);
1372 EXPECT_EQ(transport1, transport3);
1373
1374 auto it = changed_rtp_transport_by_mid_.find(kVideoMid1);
1375 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1376 EXPECT_EQ(transport1, it->second);
1377 it = changed_rtp_transport_by_mid_.find(kAudioMid2);
Zhi Huangd2248f82018-04-10 14:41:03 -07001378 EXPECT_TRUE(transport2 == it->second);
Zhi Huange818b6e2018-02-22 15:26:27 -08001379}
1380
1381// Tests that the initial offer/answer only have data section and audio/video
1382// sections are added in the subsequent offer.
1383TEST_F(JsepTransportControllerTest, BundleOnDataSectionInSubsequentOffer) {
1384 CreateJsepTransportController(JsepTransportController::Config());
1385 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1386 bundle_group.AddContentName(kDataMid1);
1387
Karl Wiberg918f50c2018-07-05 11:40:33 +02001388 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001389 AddDataSection(local_offer.get(), kDataMid1,
1390 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
1391 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1392 nullptr);
Karl Wiberg918f50c2018-07-05 11:40:33 +02001393 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001394 AddDataSection(remote_answer.get(), kDataMid1,
1395 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
1396 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1397 nullptr);
1398 local_offer->AddGroup(bundle_group);
1399 remote_answer->AddGroup(bundle_group);
1400
1401 EXPECT_TRUE(transport_controller_
1402 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1403 .ok());
1404 EXPECT_TRUE(transport_controller_
1405 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1406 .ok());
1407 auto data_transport = transport_controller_->GetRtpTransport(kDataMid1);
1408
1409 // Add audio/video sections in subsequent offer.
1410 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1411 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1412 nullptr);
1413 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1414 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1415 nullptr);
1416 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1417 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1418 nullptr);
1419 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1420 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1421 nullptr);
1422
1423 // Reset the bundle group and do another offer/answer exchange.
1424 bundle_group.AddContentName(kAudioMid1);
1425 bundle_group.AddContentName(kVideoMid1);
1426 local_offer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1427 remote_answer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1428 local_offer->AddGroup(bundle_group);
1429 remote_answer->AddGroup(bundle_group);
1430
1431 EXPECT_TRUE(transport_controller_
1432 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1433 .ok());
1434 EXPECT_TRUE(transport_controller_
1435 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1436 .ok());
1437
1438 auto audio_transport = transport_controller_->GetRtpTransport(kAudioMid1);
1439 auto video_transport = transport_controller_->GetRtpTransport(kVideoMid1);
1440 EXPECT_EQ(data_transport, audio_transport);
1441 EXPECT_EQ(data_transport, video_transport);
1442}
1443
1444TEST_F(JsepTransportControllerTest, VideoDataRejectedInAnswer) {
1445 CreateJsepTransportController(JsepTransportController::Config());
1446 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1447 bundle_group.AddContentName(kAudioMid1);
1448 bundle_group.AddContentName(kVideoMid1);
1449 bundle_group.AddContentName(kDataMid1);
1450
Karl Wiberg918f50c2018-07-05 11:40:33 +02001451 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001452 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1453 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1454 nullptr);
1455 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1456 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1457 nullptr);
1458 AddDataSection(local_offer.get(), kDataMid1,
1459 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1460 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1461 nullptr);
1462
Karl Wiberg918f50c2018-07-05 11:40:33 +02001463 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001464 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1465 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1466 nullptr);
1467 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1468 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1469 nullptr);
1470 AddDataSection(remote_answer.get(), kDataMid1,
1471 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1472 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1473 nullptr);
1474 // Reject video and data section.
1475 remote_answer->contents()[1].rejected = true;
1476 remote_answer->contents()[2].rejected = true;
1477
1478 local_offer->AddGroup(bundle_group);
1479 remote_answer->AddGroup(bundle_group);
1480
1481 EXPECT_TRUE(transport_controller_
1482 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1483 .ok());
1484 EXPECT_TRUE(transport_controller_
1485 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1486 .ok());
1487
1488 // Verify the RtpTransport/DtlsTransport is destroyed correctly.
1489 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kVideoMid1));
1490 EXPECT_EQ(nullptr, transport_controller_->GetDtlsTransport(kDataMid1));
1491 // Verify the signals are fired correctly.
1492 auto it = changed_rtp_transport_by_mid_.find(kVideoMid1);
1493 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1494 EXPECT_EQ(nullptr, it->second);
1495 auto it2 = changed_dtls_transport_by_mid_.find(kDataMid1);
1496 ASSERT_TRUE(it2 != changed_dtls_transport_by_mid_.end());
1497 EXPECT_EQ(nullptr, it2->second);
1498}
1499
1500// Tests that changing the bundled MID in subsequent offer/answer exchange is
1501// not supported.
1502// TODO(bugs.webrtc.org/6704): Change this test to expect success once issue is
1503// fixed
1504TEST_F(JsepTransportControllerTest, ChangeBundledMidNotSupported) {
1505 CreateJsepTransportController(JsepTransportController::Config());
1506 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1507 bundle_group.AddContentName(kAudioMid1);
1508 bundle_group.AddContentName(kVideoMid1);
1509
Karl Wiberg918f50c2018-07-05 11:40:33 +02001510 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001511 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1512 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1513 nullptr);
1514 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1515 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1516 nullptr);
1517
Karl Wiberg918f50c2018-07-05 11:40:33 +02001518 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001519 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1520 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1521 nullptr);
1522 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1523 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1524 nullptr);
1525
1526 local_offer->AddGroup(bundle_group);
1527 remote_answer->AddGroup(bundle_group);
1528 EXPECT_TRUE(transport_controller_
1529 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1530 .ok());
1531 EXPECT_TRUE(transport_controller_
1532 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1533 .ok());
1534 EXPECT_EQ(transport_controller_->GetRtpTransport(kAudioMid1),
1535 transport_controller_->GetRtpTransport(kVideoMid1));
1536
1537 // Reorder the bundle group.
1538 EXPECT_TRUE(bundle_group.RemoveContentName(kAudioMid1));
1539 bundle_group.AddContentName(kAudioMid1);
1540 // The answerer uses the new bundle group and now the bundle mid is changed to
1541 // |kVideo1|.
1542 remote_answer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1543 remote_answer->AddGroup(bundle_group);
1544 EXPECT_TRUE(transport_controller_
1545 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1546 .ok());
1547 EXPECT_FALSE(transport_controller_
1548 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1549 .ok());
1550}
Zhi Huange830e682018-03-30 10:48:35 -07001551// Test that rejecting only the first m= section of a BUNDLE group is treated as
1552// an error, but rejecting all of them works as expected.
1553TEST_F(JsepTransportControllerTest, RejectFirstContentInBundleGroup) {
1554 CreateJsepTransportController(JsepTransportController::Config());
1555 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1556 bundle_group.AddContentName(kAudioMid1);
1557 bundle_group.AddContentName(kVideoMid1);
1558 bundle_group.AddContentName(kDataMid1);
1559
Karl Wiberg918f50c2018-07-05 11:40:33 +02001560 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange830e682018-03-30 10:48:35 -07001561 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1562 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1563 nullptr);
1564 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1565 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1566 nullptr);
1567 AddDataSection(local_offer.get(), kDataMid1,
1568 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1569 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1570 nullptr);
1571
Karl Wiberg918f50c2018-07-05 11:40:33 +02001572 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange830e682018-03-30 10:48:35 -07001573 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1574 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1575 nullptr);
1576 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1577 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1578 nullptr);
1579 AddDataSection(remote_answer.get(), kDataMid1,
1580 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1581 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1582 nullptr);
1583 // Reject audio content in answer.
1584 remote_answer->contents()[0].rejected = true;
1585
1586 local_offer->AddGroup(bundle_group);
1587 remote_answer->AddGroup(bundle_group);
1588
1589 EXPECT_TRUE(transport_controller_
1590 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1591 .ok());
1592 EXPECT_FALSE(transport_controller_
1593 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1594 .ok());
1595
1596 // Reject all the contents.
1597 remote_answer->contents()[1].rejected = true;
1598 remote_answer->contents()[2].rejected = true;
1599 EXPECT_TRUE(transport_controller_
1600 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1601 .ok());
1602 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kAudioMid1));
1603 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kVideoMid1));
1604 EXPECT_EQ(nullptr, transport_controller_->GetDtlsTransport(kDataMid1));
1605}
1606
1607// Tests that applying non-RTCP-mux offer would fail when kRtcpMuxPolicyRequire
1608// is used.
1609TEST_F(JsepTransportControllerTest, ApplyNonRtcpMuxOfferWhenMuxingRequired) {
1610 JsepTransportController::Config config;
1611 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
1612 CreateJsepTransportController(config);
Karl Wiberg918f50c2018-07-05 11:40:33 +02001613 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange830e682018-03-30 10:48:35 -07001614 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1615 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1616 nullptr);
1617
1618 local_offer->contents()[0].media_description()->set_rtcp_mux(false);
1619 // Applying a non-RTCP-mux offer is expected to fail.
1620 EXPECT_FALSE(transport_controller_
1621 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1622 .ok());
1623}
1624
1625// Tests that applying non-RTCP-mux answer would fail when kRtcpMuxPolicyRequire
1626// is used.
1627TEST_F(JsepTransportControllerTest, ApplyNonRtcpMuxAnswerWhenMuxingRequired) {
1628 JsepTransportController::Config config;
1629 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
1630 CreateJsepTransportController(config);
Karl Wiberg918f50c2018-07-05 11:40:33 +02001631 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange830e682018-03-30 10:48:35 -07001632 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1633 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1634 nullptr);
1635 EXPECT_TRUE(transport_controller_
1636 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1637 .ok());
1638
Karl Wiberg918f50c2018-07-05 11:40:33 +02001639 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange830e682018-03-30 10:48:35 -07001640 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1641 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1642 nullptr);
1643 // Applying a non-RTCP-mux answer is expected to fail.
1644 remote_answer->contents()[0].media_description()->set_rtcp_mux(false);
1645 EXPECT_FALSE(transport_controller_
1646 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1647 .ok());
1648}
Zhi Huange818b6e2018-02-22 15:26:27 -08001649
Zhi Huangd2248f82018-04-10 14:41:03 -07001650// This tests that the BUNDLE group in answer should be a subset of the offered
1651// group.
1652TEST_F(JsepTransportControllerTest,
1653 AddContentToBundleGroupInAnswerNotSupported) {
1654 CreateJsepTransportController(JsepTransportController::Config());
1655 auto local_offer = CreateSessionDescriptionWithoutBundle();
1656 auto remote_answer = CreateSessionDescriptionWithoutBundle();
1657
1658 cricket::ContentGroup offer_bundle_group(cricket::GROUP_TYPE_BUNDLE);
1659 offer_bundle_group.AddContentName(kAudioMid1);
1660 local_offer->AddGroup(offer_bundle_group);
1661
1662 cricket::ContentGroup answer_bundle_group(cricket::GROUP_TYPE_BUNDLE);
1663 answer_bundle_group.AddContentName(kAudioMid1);
1664 answer_bundle_group.AddContentName(kVideoMid1);
1665 remote_answer->AddGroup(answer_bundle_group);
1666 EXPECT_TRUE(transport_controller_
1667 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1668 .ok());
1669 EXPECT_FALSE(transport_controller_
1670 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1671 .ok());
1672}
1673
1674// This tests that the BUNDLE group with non-existing MID should be rejectd.
1675TEST_F(JsepTransportControllerTest, RejectBundleGroupWithNonExistingMid) {
1676 CreateJsepTransportController(JsepTransportController::Config());
1677 auto local_offer = CreateSessionDescriptionWithoutBundle();
1678 auto remote_answer = CreateSessionDescriptionWithoutBundle();
1679
1680 cricket::ContentGroup invalid_bundle_group(cricket::GROUP_TYPE_BUNDLE);
1681 // The BUNDLE group is invalid because there is no data section in the
1682 // description.
1683 invalid_bundle_group.AddContentName(kDataMid1);
1684 local_offer->AddGroup(invalid_bundle_group);
1685 remote_answer->AddGroup(invalid_bundle_group);
1686
1687 EXPECT_FALSE(transport_controller_
1688 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1689 .ok());
1690 EXPECT_FALSE(transport_controller_
1691 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1692 .ok());
1693}
1694
1695// This tests that an answer shouldn't be able to remove an m= section from an
1696// established group without rejecting it.
1697TEST_F(JsepTransportControllerTest, RemoveContentFromBundleGroup) {
1698 CreateJsepTransportController(JsepTransportController::Config());
1699
1700 auto local_offer = CreateSessionDescriptionWithBundleGroup();
1701 auto remote_answer = CreateSessionDescriptionWithBundleGroup();
1702 EXPECT_TRUE(transport_controller_
1703 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1704 .ok());
1705 EXPECT_TRUE(transport_controller_
1706 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1707 .ok());
1708
1709 // Do an re-offer/answer.
1710 EXPECT_TRUE(transport_controller_
1711 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1712 .ok());
1713 auto new_answer = CreateSessionDescriptionWithoutBundle();
1714 cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
1715 // The answer removes video from the BUNDLE group without rejecting it is
1716 // invalid.
1717 new_bundle_group.AddContentName(kAudioMid1);
1718 new_answer->AddGroup(new_bundle_group);
1719
1720 // Applying invalid answer is expected to fail.
1721 EXPECT_FALSE(transport_controller_
1722 ->SetRemoteDescription(SdpType::kAnswer, new_answer.get())
1723 .ok());
1724
1725 // Rejected the video content.
1726 auto video_content = new_answer->GetContentByName(kVideoMid1);
1727 ASSERT_TRUE(video_content);
1728 video_content->rejected = true;
1729 EXPECT_TRUE(transport_controller_
1730 ->SetRemoteDescription(SdpType::kAnswer, new_answer.get())
1731 .ok());
1732}
1733
Steve Anton2bed3972019-01-04 17:04:30 -08001734// Test that the JsepTransportController can process a new local and remote
1735// description that changes the tagged BUNDLE group with the max-bundle policy
1736// specified.
1737// This is a regression test for bugs.webrtc.org/9954
1738TEST_F(JsepTransportControllerTest, ChangeTaggedMediaSectionMaxBundle) {
1739 CreateJsepTransportController(JsepTransportController::Config());
1740
1741 auto local_offer = absl::make_unique<cricket::SessionDescription>();
1742 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1743 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1744 nullptr);
1745 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1746 bundle_group.AddContentName(kAudioMid1);
1747 local_offer->AddGroup(bundle_group);
1748 EXPECT_TRUE(transport_controller_
1749 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1750 .ok());
1751
1752 std::unique_ptr<cricket::SessionDescription> remote_answer(
1753 local_offer->Copy());
1754 EXPECT_TRUE(transport_controller_
1755 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1756 .ok());
1757
1758 std::unique_ptr<cricket::SessionDescription> local_reoffer(
1759 local_offer->Copy());
1760 local_reoffer->contents()[0].rejected = true;
1761 AddVideoSection(local_reoffer.get(), kVideoMid1, kIceUfrag1, kIcePwd1,
1762 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1763 nullptr);
1764 local_reoffer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1765 cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
1766 new_bundle_group.AddContentName(kVideoMid1);
1767 local_reoffer->AddGroup(new_bundle_group);
1768
1769 EXPECT_TRUE(transport_controller_
1770 ->SetLocalDescription(SdpType::kOffer, local_reoffer.get())
1771 .ok());
1772
1773 std::unique_ptr<cricket::SessionDescription> remote_reanswer(
1774 local_reoffer->Copy());
1775 EXPECT_TRUE(
1776 transport_controller_
1777 ->SetRemoteDescription(SdpType::kAnswer, remote_reanswer.get())
1778 .ok());
1779}
1780
Zhi Huange818b6e2018-02-22 15:26:27 -08001781} // namespace webrtc