blob: 08b1f9bd134c8b30ee9997dc08898427db74fa43 [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
Anton Sukhanov7940da02018-10-10 10:34:49 -070014#include "api/test/fake_media_transport.h"
Zhi Huange818b6e2018-02-22 15:26:27 -080015#include "p2p/base/fakedtlstransport.h"
16#include "p2p/base/fakeicetransport.h"
17#include "p2p/base/transportfactoryinterface.h"
18#include "p2p/base/transportinfo.h"
19#include "pc/jseptransportcontroller.h"
20#include "rtc_base/gunit.h"
Zhi Huange818b6e2018-02-22 15:26:27 -080021#include "rtc_base/thread.h"
22#include "test/gtest.h"
23
24using cricket::FakeDtlsTransport;
25using cricket::Candidate;
26using cricket::Candidates;
27using 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
Piotr (Peter) Slatala9f956252018-10-31 08:25:26 -070044namespace {
45
46// Media transport factory requires crypto settings to be present in order to
47// create media transport.
48void AddCryptoSettings(cricket::SessionDescription* description) {
49 for (auto& content : description->contents()) {
50 content.media_description()->AddCrypto(cricket::CryptoParams(
51 /*t=*/0, std::string(rtc::CS_AES_CM_128_HMAC_SHA1_80),
52 "inline:YUJDZGVmZ2hpSktMbW9QUXJzVHVWd3l6MTIzNDU2", ""));
53 }
54}
55
56} // namespace
57
Zhi Huange818b6e2018-02-22 15:26:27 -080058class FakeTransportFactory : public cricket::TransportFactoryInterface {
59 public:
60 std::unique_ptr<cricket::IceTransportInternal> CreateIceTransport(
61 const std::string& transport_name,
62 int component) override {
Karl Wiberg918f50c2018-07-05 11:40:33 +020063 return absl::make_unique<cricket::FakeIceTransport>(transport_name,
64 component);
Zhi Huange818b6e2018-02-22 15:26:27 -080065 }
66
67 std::unique_ptr<cricket::DtlsTransportInternal> CreateDtlsTransport(
68 std::unique_ptr<cricket::IceTransportInternal> ice,
Benjamin Wrighta54daf12018-10-11 15:33:17 -070069 const webrtc::CryptoOptions& crypto_options) override {
Zhi Huange818b6e2018-02-22 15:26:27 -080070 std::unique_ptr<cricket::FakeIceTransport> fake_ice(
71 static_cast<cricket::FakeIceTransport*>(ice.release()));
Karl Wiberg918f50c2018-07-05 11:40:33 +020072 return absl::make_unique<FakeDtlsTransport>(std::move(fake_ice));
Zhi Huange818b6e2018-02-22 15:26:27 -080073 }
74};
75
Zhi Huang365381f2018-04-13 16:44:34 -070076class JsepTransportControllerTest : public JsepTransportController::Observer,
77 public testing::Test,
Zhi Huange818b6e2018-02-22 15:26:27 -080078 public sigslot::has_slots<> {
79 public:
80 JsepTransportControllerTest() : signaling_thread_(rtc::Thread::Current()) {
Karl Wiberg918f50c2018-07-05 11:40:33 +020081 fake_transport_factory_ = absl::make_unique<FakeTransportFactory>();
Zhi Huange818b6e2018-02-22 15:26:27 -080082 }
83
84 void CreateJsepTransportController(
85 JsepTransportController::Config config,
86 rtc::Thread* signaling_thread = rtc::Thread::Current(),
87 rtc::Thread* network_thread = rtc::Thread::Current(),
88 cricket::PortAllocator* port_allocator = nullptr) {
Zhi Huang365381f2018-04-13 16:44:34 -070089 config.transport_observer = this;
Zhi Huange818b6e2018-02-22 15:26:27 -080090 // The tests only works with |fake_transport_factory|;
91 config.external_transport_factory = fake_transport_factory_.get();
Zach Steine20867f2018-08-02 13:20:15 -070092 // TODO(zstein): Provide an AsyncResolverFactory once it is required.
Karl Wiberg918f50c2018-07-05 11:40:33 +020093 transport_controller_ = absl::make_unique<JsepTransportController>(
Zach Steine20867f2018-08-02 13:20:15 -070094 signaling_thread, network_thread, port_allocator, nullptr, config);
Zhi Huange818b6e2018-02-22 15:26:27 -080095 ConnectTransportControllerSignals();
96 }
97
98 void ConnectTransportControllerSignals() {
99 transport_controller_->SignalIceConnectionState.connect(
100 this, &JsepTransportControllerTest::OnConnectionState);
Jonas Olsson635474e2018-10-18 15:58:17 +0200101 transport_controller_->SignalStandardizedIceConnectionState.connect(
102 this, &JsepTransportControllerTest::OnStandardizedIceConnectionState);
103 transport_controller_->SignalConnectionState.connect(
104 this, &JsepTransportControllerTest::OnCombinedConnectionState);
Zhi Huange818b6e2018-02-22 15:26:27 -0800105 transport_controller_->SignalIceGatheringState.connect(
106 this, &JsepTransportControllerTest::OnGatheringState);
107 transport_controller_->SignalIceCandidatesGathered.connect(
108 this, &JsepTransportControllerTest::OnCandidatesGathered);
Zhi Huange818b6e2018-02-22 15:26:27 -0800109 }
110
111 std::unique_ptr<cricket::SessionDescription>
112 CreateSessionDescriptionWithoutBundle() {
Karl Wiberg918f50c2018-07-05 11:40:33 +0200113 auto description = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800114 AddAudioSection(description.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
115 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
116 nullptr);
117 AddVideoSection(description.get(), kVideoMid1, kIceUfrag1, kIcePwd1,
118 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
119 nullptr);
120 return description;
121 }
122
123 std::unique_ptr<cricket::SessionDescription>
124 CreateSessionDescriptionWithBundleGroup() {
125 auto description = CreateSessionDescriptionWithoutBundle();
126 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
127 bundle_group.AddContentName(kAudioMid1);
128 bundle_group.AddContentName(kVideoMid1);
129 description->AddGroup(bundle_group);
130
131 return description;
132 }
133
134 void AddAudioSection(cricket::SessionDescription* description,
135 const std::string& mid,
136 const std::string& ufrag,
137 const std::string& pwd,
138 cricket::IceMode ice_mode,
139 cricket::ConnectionRole conn_role,
140 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
141 std::unique_ptr<cricket::AudioContentDescription> audio(
142 new cricket::AudioContentDescription());
Zhi Huange830e682018-03-30 10:48:35 -0700143 // Set RTCP-mux to be true because the default policy is "mux required".
144 audio->set_rtcp_mux(true);
Zhi Huange818b6e2018-02-22 15:26:27 -0800145 description->AddContent(mid, cricket::MediaProtocolType::kRtp,
146 /*rejected=*/false, audio.release());
147 AddTransportInfo(description, mid, ufrag, pwd, ice_mode, conn_role, cert);
148 }
149
150 void AddVideoSection(cricket::SessionDescription* description,
151 const std::string& mid,
152 const std::string& ufrag,
153 const std::string& pwd,
154 cricket::IceMode ice_mode,
155 cricket::ConnectionRole conn_role,
156 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
157 std::unique_ptr<cricket::VideoContentDescription> video(
158 new cricket::VideoContentDescription());
Zhi Huange830e682018-03-30 10:48:35 -0700159 // Set RTCP-mux to be true because the default policy is "mux required".
160 video->set_rtcp_mux(true);
Zhi Huange818b6e2018-02-22 15:26:27 -0800161 description->AddContent(mid, cricket::MediaProtocolType::kRtp,
162 /*rejected=*/false, video.release());
163 AddTransportInfo(description, mid, ufrag, pwd, ice_mode, conn_role, cert);
164 }
165
166 void AddDataSection(cricket::SessionDescription* description,
167 const std::string& mid,
168 cricket::MediaProtocolType protocol_type,
169 const std::string& ufrag,
170 const std::string& pwd,
171 cricket::IceMode ice_mode,
172 cricket::ConnectionRole conn_role,
173 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
174 std::unique_ptr<cricket::DataContentDescription> data(
175 new cricket::DataContentDescription());
Zhi Huange830e682018-03-30 10:48:35 -0700176 data->set_rtcp_mux(true);
Zhi Huange818b6e2018-02-22 15:26:27 -0800177 description->AddContent(mid, protocol_type,
178 /*rejected=*/false, data.release());
179 AddTransportInfo(description, mid, ufrag, pwd, ice_mode, conn_role, cert);
180 }
181
182 void AddTransportInfo(cricket::SessionDescription* description,
183 const std::string& mid,
184 const std::string& ufrag,
185 const std::string& pwd,
186 cricket::IceMode ice_mode,
187 cricket::ConnectionRole conn_role,
188 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
189 std::unique_ptr<rtc::SSLFingerprint> fingerprint;
190 if (cert) {
Steve Anton4905edb2018-10-15 19:27:44 -0700191 fingerprint = rtc::SSLFingerprint::CreateFromCertificate(*cert);
Zhi Huange818b6e2018-02-22 15:26:27 -0800192 }
193
194 cricket::TransportDescription transport_desc(std::vector<std::string>(),
195 ufrag, pwd, ice_mode,
196 conn_role, fingerprint.get());
197 description->AddTransportInfo(cricket::TransportInfo(mid, transport_desc));
198 }
199
200 cricket::IceConfig CreateIceConfig(
201 int receiving_timeout,
202 cricket::ContinualGatheringPolicy continual_gathering_policy) {
203 cricket::IceConfig config;
204 config.receiving_timeout = receiving_timeout;
205 config.continual_gathering_policy = continual_gathering_policy;
206 return config;
207 }
208
209 Candidate CreateCandidate(const std::string& transport_name, int component) {
210 Candidate c;
211 c.set_transport_name(transport_name);
212 c.set_address(rtc::SocketAddress("192.168.1.1", 8000));
213 c.set_component(component);
214 c.set_protocol(cricket::UDP_PROTOCOL_NAME);
215 c.set_priority(1);
216 return c;
217 }
218
219 void CreateLocalDescriptionAndCompleteConnectionOnNetworkThread() {
220 if (!network_thread_->IsCurrent()) {
221 network_thread_->Invoke<void>(RTC_FROM_HERE, [&] {
222 CreateLocalDescriptionAndCompleteConnectionOnNetworkThread();
223 });
224 return;
225 }
226
227 auto description = CreateSessionDescriptionWithBundleGroup();
228 EXPECT_TRUE(transport_controller_
229 ->SetLocalDescription(SdpType::kOffer, description.get())
230 .ok());
231
232 transport_controller_->MaybeStartGathering();
233 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
234 transport_controller_->GetDtlsTransport(kAudioMid1));
235 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
236 transport_controller_->GetDtlsTransport(kVideoMid1));
237 fake_audio_dtls->fake_ice_transport()->SignalCandidateGathered(
238 fake_audio_dtls->fake_ice_transport(),
239 CreateCandidate(kAudioMid1, /*component=*/1));
240 fake_video_dtls->fake_ice_transport()->SignalCandidateGathered(
241 fake_video_dtls->fake_ice_transport(),
242 CreateCandidate(kVideoMid1, /*component=*/1));
243 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
244 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
245 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(2);
246 fake_video_dtls->fake_ice_transport()->SetConnectionCount(2);
247 fake_audio_dtls->SetReceiving(true);
248 fake_video_dtls->SetReceiving(true);
249 fake_audio_dtls->SetWritable(true);
250 fake_video_dtls->SetWritable(true);
251 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
252 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
253 }
254
255 protected:
256 void OnConnectionState(cricket::IceConnectionState state) {
257 if (!signaling_thread_->IsCurrent()) {
258 signaled_on_non_signaling_thread_ = true;
259 }
260 connection_state_ = state;
261 ++connection_state_signal_count_;
262 }
263
Jonas Olsson635474e2018-10-18 15:58:17 +0200264 void OnStandardizedIceConnectionState(
265 PeerConnectionInterface::IceConnectionState state) {
266 if (!signaling_thread_->IsCurrent()) {
267 signaled_on_non_signaling_thread_ = true;
268 }
269 ice_connection_state_ = state;
270 ++ice_connection_state_signal_count_;
271 }
272
273 void OnCombinedConnectionState(
274 PeerConnectionInterface::PeerConnectionState state) {
275 if (!signaling_thread_->IsCurrent()) {
276 signaled_on_non_signaling_thread_ = true;
277 }
278 combined_connection_state_ = state;
279 ++combined_connection_state_signal_count_;
280 }
281
Zhi Huange818b6e2018-02-22 15:26:27 -0800282 void OnGatheringState(cricket::IceGatheringState state) {
283 if (!signaling_thread_->IsCurrent()) {
284 signaled_on_non_signaling_thread_ = true;
285 }
286 gathering_state_ = state;
287 ++gathering_state_signal_count_;
288 }
289
290 void OnCandidatesGathered(const std::string& transport_name,
291 const Candidates& candidates) {
292 if (!signaling_thread_->IsCurrent()) {
293 signaled_on_non_signaling_thread_ = true;
294 }
295 candidates_[transport_name].insert(candidates_[transport_name].end(),
296 candidates.begin(), candidates.end());
297 ++candidates_signal_count_;
298 }
299
Zhi Huang365381f2018-04-13 16:44:34 -0700300 // JsepTransportController::Observer overrides.
Taylor Brandstettercbaa2542018-04-16 16:42:14 -0700301 bool OnTransportChanged(
Zhi Huang365381f2018-04-13 16:44:34 -0700302 const std::string& mid,
Taylor Brandstettercbaa2542018-04-16 16:42:14 -0700303 RtpTransportInternal* rtp_transport,
Zhi Huang365381f2018-04-13 16:44:34 -0700304 cricket::DtlsTransportInternal* dtls_transport) override {
Taylor Brandstettercbaa2542018-04-16 16:42:14 -0700305 changed_rtp_transport_by_mid_[mid] = rtp_transport;
Zhi Huange818b6e2018-02-22 15:26:27 -0800306 changed_dtls_transport_by_mid_[mid] = dtls_transport;
Taylor Brandstettercbaa2542018-04-16 16:42:14 -0700307 return true;
Zhi Huange818b6e2018-02-22 15:26:27 -0800308 }
309
310 // Information received from signals from transport controller.
311 cricket::IceConnectionState connection_state_ =
312 cricket::kIceConnectionConnecting;
Jonas Olsson635474e2018-10-18 15:58:17 +0200313 PeerConnectionInterface::IceConnectionState ice_connection_state_ =
314 PeerConnectionInterface::kIceConnectionNew;
315 PeerConnectionInterface::PeerConnectionState combined_connection_state_ =
316 PeerConnectionInterface::PeerConnectionState::kNew;
Zhi Huange818b6e2018-02-22 15:26:27 -0800317 bool receiving_ = false;
318 cricket::IceGatheringState gathering_state_ = cricket::kIceGatheringNew;
319 // transport_name => candidates
320 std::map<std::string, Candidates> candidates_;
321 // Counts of each signal emitted.
322 int connection_state_signal_count_ = 0;
Jonas Olsson635474e2018-10-18 15:58:17 +0200323 int ice_connection_state_signal_count_ = 0;
324 int combined_connection_state_signal_count_ = 0;
Zhi Huange818b6e2018-02-22 15:26:27 -0800325 int receiving_signal_count_ = 0;
326 int gathering_state_signal_count_ = 0;
327 int candidates_signal_count_ = 0;
328
329 // |network_thread_| should be destroyed after |transport_controller_|
330 std::unique_ptr<rtc::Thread> network_thread_;
331 std::unique_ptr<JsepTransportController> transport_controller_;
332 std::unique_ptr<FakeTransportFactory> fake_transport_factory_;
333 rtc::Thread* const signaling_thread_ = nullptr;
334 bool signaled_on_non_signaling_thread_ = false;
335 // Used to verify the SignalRtpTransportChanged/SignalDtlsTransportChanged are
336 // signaled correctly.
337 std::map<std::string, RtpTransportInternal*> changed_rtp_transport_by_mid_;
338 std::map<std::string, cricket::DtlsTransportInternal*>
339 changed_dtls_transport_by_mid_;
340};
341
342TEST_F(JsepTransportControllerTest, GetRtpTransport) {
343 CreateJsepTransportController(JsepTransportController::Config());
344 auto description = CreateSessionDescriptionWithoutBundle();
345 EXPECT_TRUE(transport_controller_
346 ->SetLocalDescription(SdpType::kOffer, description.get())
347 .ok());
348 auto audio_rtp_transport = transport_controller_->GetRtpTransport(kAudioMid1);
349 auto video_rtp_transport = transport_controller_->GetRtpTransport(kVideoMid1);
350 EXPECT_NE(nullptr, audio_rtp_transport);
351 EXPECT_NE(nullptr, video_rtp_transport);
352 EXPECT_NE(audio_rtp_transport, video_rtp_transport);
353 // Return nullptr for non-existing ones.
354 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kAudioMid2));
355}
356
357TEST_F(JsepTransportControllerTest, GetDtlsTransport) {
358 JsepTransportController::Config config;
359 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
360 CreateJsepTransportController(config);
361 auto description = CreateSessionDescriptionWithoutBundle();
362 EXPECT_TRUE(transport_controller_
363 ->SetLocalDescription(SdpType::kOffer, description.get())
364 .ok());
365 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kAudioMid1));
366 EXPECT_NE(nullptr, transport_controller_->GetRtcpDtlsTransport(kAudioMid1));
367 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kVideoMid1));
368 EXPECT_NE(nullptr, transport_controller_->GetRtcpDtlsTransport(kVideoMid1));
369 // Return nullptr for non-existing ones.
370 EXPECT_EQ(nullptr, transport_controller_->GetDtlsTransport(kVideoMid2));
371 EXPECT_EQ(nullptr, transport_controller_->GetRtcpDtlsTransport(kVideoMid2));
372}
373
Zhi Huange830e682018-03-30 10:48:35 -0700374TEST_F(JsepTransportControllerTest, GetDtlsTransportWithRtcpMux) {
375 JsepTransportController::Config config;
376 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
377 CreateJsepTransportController(config);
378 auto description = CreateSessionDescriptionWithoutBundle();
379 EXPECT_TRUE(transport_controller_
380 ->SetLocalDescription(SdpType::kOffer, description.get())
381 .ok());
382 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kAudioMid1));
383 EXPECT_EQ(nullptr, transport_controller_->GetRtcpDtlsTransport(kAudioMid1));
384 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kVideoMid1));
385 EXPECT_EQ(nullptr, transport_controller_->GetRtcpDtlsTransport(kVideoMid1));
Anton Sukhanov7940da02018-10-10 10:34:49 -0700386 EXPECT_EQ(nullptr, transport_controller_->GetMediaTransport(kAudioMid1));
387}
388
389TEST_F(JsepTransportControllerTest, GetMediaTransportInCaller) {
390 FakeMediaTransportFactory fake_media_transport_factory;
391 JsepTransportController::Config config;
392
393 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
394 config.media_transport_factory = &fake_media_transport_factory;
395 CreateJsepTransportController(config);
396 auto description = CreateSessionDescriptionWithoutBundle();
Piotr (Peter) Slatala9f956252018-10-31 08:25:26 -0700397 AddCryptoSettings(description.get());
398
Anton Sukhanov7940da02018-10-10 10:34:49 -0700399 EXPECT_TRUE(transport_controller_
400 ->SetLocalDescription(SdpType::kOffer, description.get())
401 .ok());
402
403 FakeMediaTransport* media_transport = static_cast<FakeMediaTransport*>(
404 transport_controller_->GetMediaTransport(kAudioMid1));
405
406 ASSERT_NE(nullptr, media_transport);
407
408 // After SetLocalDescription, media transport should be created as caller.
409 EXPECT_TRUE(media_transport->is_caller());
Piotr (Peter) Slatala9f956252018-10-31 08:25:26 -0700410 EXPECT_TRUE(media_transport->pre_shared_key().has_value());
Anton Sukhanov7940da02018-10-10 10:34:49 -0700411
412 // Return nullptr for non-existing mids.
413 EXPECT_EQ(nullptr, transport_controller_->GetMediaTransport(kVideoMid2));
414}
415
416TEST_F(JsepTransportControllerTest, GetMediaTransportInCallee) {
417 FakeMediaTransportFactory fake_media_transport_factory;
418 JsepTransportController::Config config;
419
420 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
421 config.media_transport_factory = &fake_media_transport_factory;
422 CreateJsepTransportController(config);
423 auto description = CreateSessionDescriptionWithoutBundle();
Piotr (Peter) Slatala9f956252018-10-31 08:25:26 -0700424 AddCryptoSettings(description.get());
Anton Sukhanov7940da02018-10-10 10:34:49 -0700425 EXPECT_TRUE(transport_controller_
426 ->SetRemoteDescription(SdpType::kOffer, description.get())
427 .ok());
428
429 FakeMediaTransport* media_transport = static_cast<FakeMediaTransport*>(
430 transport_controller_->GetMediaTransport(kAudioMid1));
431
432 ASSERT_NE(nullptr, media_transport);
433
434 // After SetRemoteDescription, media transport should be created as callee.
435 EXPECT_FALSE(media_transport->is_caller());
Piotr (Peter) Slatala9f956252018-10-31 08:25:26 -0700436 EXPECT_TRUE(media_transport->pre_shared_key().has_value());
Anton Sukhanov7940da02018-10-10 10:34:49 -0700437
438 // Return nullptr for non-existing mids.
439 EXPECT_EQ(nullptr, transport_controller_->GetMediaTransport(kVideoMid2));
Zhi Huange830e682018-03-30 10:48:35 -0700440}
441
Piotr (Peter) Slatala9f956252018-10-31 08:25:26 -0700442TEST_F(JsepTransportControllerTest, GetMediaTransportIsNotSetIfNoSdes) {
443 FakeMediaTransportFactory fake_media_transport_factory;
444 JsepTransportController::Config config;
445
446 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
447 config.media_transport_factory = &fake_media_transport_factory;
448 CreateJsepTransportController(config);
449 auto description = CreateSessionDescriptionWithoutBundle();
450 EXPECT_TRUE(transport_controller_
451 ->SetRemoteDescription(SdpType::kOffer, description.get())
452 .ok());
453
454 EXPECT_EQ(nullptr, transport_controller_->GetMediaTransport(kAudioMid1));
455
456 // Even if we set local description with crypto now (after the remote offer
457 // was set), media transport won't be provided.
458 auto description2 = CreateSessionDescriptionWithoutBundle();
459 AddCryptoSettings(description2.get());
460 EXPECT_TRUE(transport_controller_
461 ->SetLocalDescription(SdpType::kAnswer, description2.get())
462 .ok());
463
464 EXPECT_EQ(nullptr, transport_controller_->GetMediaTransport(kAudioMid1));
465}
466
467TEST_F(JsepTransportControllerTest,
468 AfterSettingAnswerTheSameMediaTransportIsReturned) {
469 FakeMediaTransportFactory fake_media_transport_factory;
470 JsepTransportController::Config config;
471
472 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
473 config.media_transport_factory = &fake_media_transport_factory;
474 CreateJsepTransportController(config);
475 auto description = CreateSessionDescriptionWithoutBundle();
476 AddCryptoSettings(description.get());
477 EXPECT_TRUE(transport_controller_
478 ->SetRemoteDescription(SdpType::kOffer, description.get())
479 .ok());
480
481 FakeMediaTransport* media_transport = static_cast<FakeMediaTransport*>(
482 transport_controller_->GetMediaTransport(kAudioMid1));
483 EXPECT_NE(nullptr, media_transport);
484 EXPECT_TRUE(media_transport->pre_shared_key().has_value());
485
486 // Even if we set local description with crypto now (after the remote offer
487 // was set), media transport won't be provided.
488 auto description2 = CreateSessionDescriptionWithoutBundle();
489 AddCryptoSettings(description2.get());
490
491 RTCError result = transport_controller_->SetLocalDescription(
492 SdpType::kAnswer, description2.get());
493 EXPECT_TRUE(result.ok()) << result.message();
494
495 // Media transport did not change.
496 EXPECT_EQ(media_transport,
497 transport_controller_->GetMediaTransport(kAudioMid1));
498}
499
Zhi Huange818b6e2018-02-22 15:26:27 -0800500TEST_F(JsepTransportControllerTest, SetIceConfig) {
501 CreateJsepTransportController(JsepTransportController::Config());
502 auto description = CreateSessionDescriptionWithoutBundle();
503 EXPECT_TRUE(transport_controller_
504 ->SetLocalDescription(SdpType::kOffer, description.get())
505 .ok());
506
507 transport_controller_->SetIceConfig(
508 CreateIceConfig(kTimeout, cricket::GATHER_CONTINUALLY));
509 FakeDtlsTransport* fake_audio_dtls = static_cast<FakeDtlsTransport*>(
510 transport_controller_->GetDtlsTransport(kAudioMid1));
511 ASSERT_NE(nullptr, fake_audio_dtls);
512 EXPECT_EQ(kTimeout,
513 fake_audio_dtls->fake_ice_transport()->receiving_timeout());
514 EXPECT_TRUE(fake_audio_dtls->fake_ice_transport()->gather_continually());
515
516 // Test that value stored in controller is applied to new transports.
517 AddAudioSection(description.get(), kAudioMid2, kIceUfrag1, kIcePwd1,
518 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
519 nullptr);
520
521 EXPECT_TRUE(transport_controller_
522 ->SetLocalDescription(SdpType::kOffer, description.get())
523 .ok());
524 fake_audio_dtls = static_cast<FakeDtlsTransport*>(
525 transport_controller_->GetDtlsTransport(kAudioMid2));
526 ASSERT_NE(nullptr, fake_audio_dtls);
527 EXPECT_EQ(kTimeout,
528 fake_audio_dtls->fake_ice_transport()->receiving_timeout());
529 EXPECT_TRUE(fake_audio_dtls->fake_ice_transport()->gather_continually());
530}
531
532// Tests the getter and setter of the ICE restart flag.
533TEST_F(JsepTransportControllerTest, NeedIceRestart) {
534 CreateJsepTransportController(JsepTransportController::Config());
535 auto description = CreateSessionDescriptionWithoutBundle();
536 EXPECT_TRUE(transport_controller_
537 ->SetLocalDescription(SdpType::kOffer, description.get())
538 .ok());
539 EXPECT_TRUE(transport_controller_
540 ->SetRemoteDescription(SdpType::kAnswer, description.get())
541 .ok());
542
543 // Initially NeedsIceRestart should return false.
544 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kAudioMid1));
545 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kVideoMid1));
546 // Set the needs-ice-restart flag and verify NeedsIceRestart starts returning
547 // true.
548 transport_controller_->SetNeedsIceRestartFlag();
549 EXPECT_TRUE(transport_controller_->NeedsIceRestart(kAudioMid1));
550 EXPECT_TRUE(transport_controller_->NeedsIceRestart(kVideoMid1));
551 // For a nonexistent transport, false should be returned.
552 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kVideoMid2));
553
554 // Reset the ice_ufrag/ice_pwd for audio.
555 auto audio_transport_info = description->GetTransportInfoByName(kAudioMid1);
556 audio_transport_info->description.ice_ufrag = kIceUfrag2;
557 audio_transport_info->description.ice_pwd = kIcePwd2;
558 EXPECT_TRUE(transport_controller_
559 ->SetLocalDescription(SdpType::kOffer, description.get())
560 .ok());
561 // Because the ICE is only restarted for audio, NeedsIceRestart is expected to
562 // return false for audio and true for video.
563 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kAudioMid1));
564 EXPECT_TRUE(transport_controller_->NeedsIceRestart(kVideoMid1));
565}
566
567TEST_F(JsepTransportControllerTest, MaybeStartGathering) {
568 CreateJsepTransportController(JsepTransportController::Config());
569 auto description = CreateSessionDescriptionWithBundleGroup();
570 EXPECT_TRUE(transport_controller_
571 ->SetLocalDescription(SdpType::kOffer, description.get())
572 .ok());
573 // After setting the local description, we should be able to start gathering
574 // candidates.
575 transport_controller_->MaybeStartGathering();
576 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
577 EXPECT_EQ(1, gathering_state_signal_count_);
578}
579
580TEST_F(JsepTransportControllerTest, AddRemoveRemoteCandidates) {
581 CreateJsepTransportController(JsepTransportController::Config());
582 auto description = CreateSessionDescriptionWithoutBundle();
583 transport_controller_->SetLocalDescription(SdpType::kOffer,
584 description.get());
585 transport_controller_->SetRemoteDescription(SdpType::kAnswer,
586 description.get());
587 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
588 transport_controller_->GetDtlsTransport(kAudioMid1));
589 ASSERT_NE(nullptr, fake_audio_dtls);
590 Candidates candidates;
591 candidates.push_back(
592 CreateCandidate(kAudioMid1, cricket::ICE_CANDIDATE_COMPONENT_RTP));
593 EXPECT_TRUE(
594 transport_controller_->AddRemoteCandidates(kAudioMid1, candidates).ok());
595 EXPECT_EQ(1U,
596 fake_audio_dtls->fake_ice_transport()->remote_candidates().size());
597
598 EXPECT_TRUE(transport_controller_->RemoveRemoteCandidates(candidates).ok());
599 EXPECT_EQ(0U,
600 fake_audio_dtls->fake_ice_transport()->remote_candidates().size());
601}
602
603TEST_F(JsepTransportControllerTest, SetAndGetLocalCertificate) {
604 CreateJsepTransportController(JsepTransportController::Config());
605
606 rtc::scoped_refptr<rtc::RTCCertificate> certificate1 =
607 rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
608 rtc::SSLIdentity::Generate("session1", rtc::KT_DEFAULT)));
609 rtc::scoped_refptr<rtc::RTCCertificate> returned_certificate;
610
Karl Wiberg918f50c2018-07-05 11:40:33 +0200611 auto description = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800612 AddAudioSection(description.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
613 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
614 certificate1);
615
616 // Apply the local certificate.
617 EXPECT_TRUE(transport_controller_->SetLocalCertificate(certificate1));
618 // Apply the local description.
619 EXPECT_TRUE(transport_controller_
620 ->SetLocalDescription(SdpType::kOffer, description.get())
621 .ok());
622 returned_certificate = transport_controller_->GetLocalCertificate(kAudioMid1);
623 EXPECT_TRUE(returned_certificate);
624 EXPECT_EQ(certificate1->identity()->certificate().ToPEMString(),
625 returned_certificate->identity()->certificate().ToPEMString());
626
627 // Should fail if called for a nonexistant transport.
628 EXPECT_EQ(nullptr, transport_controller_->GetLocalCertificate(kVideoMid1));
629
630 // Shouldn't be able to change the identity once set.
631 rtc::scoped_refptr<rtc::RTCCertificate> certificate2 =
632 rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
633 rtc::SSLIdentity::Generate("session2", rtc::KT_DEFAULT)));
634 EXPECT_FALSE(transport_controller_->SetLocalCertificate(certificate2));
635}
636
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800637TEST_F(JsepTransportControllerTest, GetRemoteSSLCertChain) {
Zhi Huange818b6e2018-02-22 15:26:27 -0800638 CreateJsepTransportController(JsepTransportController::Config());
639 auto description = CreateSessionDescriptionWithBundleGroup();
640 EXPECT_TRUE(transport_controller_
641 ->SetLocalDescription(SdpType::kOffer, description.get())
642 .ok());
643 rtc::FakeSSLCertificate fake_certificate("fake_data");
644
645 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
646 transport_controller_->GetDtlsTransport(kAudioMid1));
647 fake_audio_dtls->SetRemoteSSLCertificate(&fake_certificate);
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800648 std::unique_ptr<rtc::SSLCertChain> returned_cert_chain =
649 transport_controller_->GetRemoteSSLCertChain(kAudioMid1);
650 ASSERT_TRUE(returned_cert_chain);
651 ASSERT_EQ(1u, returned_cert_chain->GetSize());
Zhi Huange818b6e2018-02-22 15:26:27 -0800652 EXPECT_EQ(fake_certificate.ToPEMString(),
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800653 returned_cert_chain->Get(0).ToPEMString());
Zhi Huange818b6e2018-02-22 15:26:27 -0800654
655 // Should fail if called for a nonexistant transport.
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800656 EXPECT_FALSE(transport_controller_->GetRemoteSSLCertChain(kAudioMid2));
Zhi Huange818b6e2018-02-22 15:26:27 -0800657}
658
659TEST_F(JsepTransportControllerTest, GetDtlsRole) {
660 CreateJsepTransportController(JsepTransportController::Config());
661 auto offer_certificate =
662 rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
663 rtc::SSLIdentity::Generate("offer", rtc::KT_DEFAULT)));
664 auto answer_certificate =
665 rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
666 rtc::SSLIdentity::Generate("answer", rtc::KT_DEFAULT)));
667 transport_controller_->SetLocalCertificate(offer_certificate);
668
Karl Wiberg918f50c2018-07-05 11:40:33 +0200669 auto offer_desc = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800670 AddAudioSection(offer_desc.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
671 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
672 offer_certificate);
Karl Wiberg918f50c2018-07-05 11:40:33 +0200673 auto answer_desc = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800674 AddAudioSection(answer_desc.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
675 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
676 answer_certificate);
677
678 EXPECT_TRUE(transport_controller_
679 ->SetLocalDescription(SdpType::kOffer, offer_desc.get())
680 .ok());
681
Danil Chapovalov66cadcc2018-06-19 16:47:43 +0200682 absl::optional<rtc::SSLRole> role =
Zhi Huange818b6e2018-02-22 15:26:27 -0800683 transport_controller_->GetDtlsRole(kAudioMid1);
684 // The DTLS role is not decided yet.
685 EXPECT_FALSE(role);
686 EXPECT_TRUE(transport_controller_
687 ->SetRemoteDescription(SdpType::kAnswer, answer_desc.get())
688 .ok());
689 role = transport_controller_->GetDtlsRole(kAudioMid1);
690
691 ASSERT_TRUE(role);
692 EXPECT_EQ(rtc::SSL_CLIENT, *role);
693}
694
695TEST_F(JsepTransportControllerTest, GetStats) {
696 CreateJsepTransportController(JsepTransportController::Config());
697 auto description = CreateSessionDescriptionWithBundleGroup();
698 EXPECT_TRUE(transport_controller_
699 ->SetLocalDescription(SdpType::kOffer, description.get())
700 .ok());
701
702 cricket::TransportStats stats;
703 EXPECT_TRUE(transport_controller_->GetStats(kAudioMid1, &stats));
704 EXPECT_EQ(kAudioMid1, stats.transport_name);
705 EXPECT_EQ(1u, stats.channel_stats.size());
706 // Return false for non-existing transport.
707 EXPECT_FALSE(transport_controller_->GetStats(kAudioMid2, &stats));
708}
709
710TEST_F(JsepTransportControllerTest, SignalConnectionStateFailed) {
711 CreateJsepTransportController(JsepTransportController::Config());
712 auto description = CreateSessionDescriptionWithoutBundle();
713 EXPECT_TRUE(transport_controller_
714 ->SetLocalDescription(SdpType::kOffer, description.get())
715 .ok());
716
717 auto fake_ice = static_cast<cricket::FakeIceTransport*>(
718 transport_controller_->GetDtlsTransport(kAudioMid1)->ice_transport());
719 fake_ice->SetCandidatesGatheringComplete();
720 fake_ice->SetConnectionCount(1);
721 // The connection stats will be failed if there is no active connection.
722 fake_ice->SetConnectionCount(0);
723 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
724 EXPECT_EQ(1, connection_state_signal_count_);
Jonas Olsson635474e2018-10-18 15:58:17 +0200725 EXPECT_EQ(PeerConnectionInterface::kIceConnectionFailed,
726 ice_connection_state_);
727 EXPECT_EQ(1, ice_connection_state_signal_count_);
728 EXPECT_EQ(PeerConnectionInterface::PeerConnectionState::kFailed,
729 combined_connection_state_);
730 EXPECT_EQ(1, combined_connection_state_signal_count_);
Zhi Huange818b6e2018-02-22 15:26:27 -0800731}
732
733TEST_F(JsepTransportControllerTest, SignalConnectionStateConnected) {
734 CreateJsepTransportController(JsepTransportController::Config());
735 auto description = CreateSessionDescriptionWithoutBundle();
736 EXPECT_TRUE(transport_controller_
737 ->SetLocalDescription(SdpType::kOffer, description.get())
738 .ok());
739
740 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
741 transport_controller_->GetDtlsTransport(kAudioMid1));
742 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
743 transport_controller_->GetDtlsTransport(kVideoMid1));
744
745 // First, have one transport connect, and another fail, to ensure that
746 // the first transport connecting didn't trigger a "connected" state signal.
747 // We should only get a signal when all are connected.
748 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
749 fake_audio_dtls->SetWritable(true);
750 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
751 // Decrease the number of the connection to trigger the signal.
752 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
753 fake_video_dtls->fake_ice_transport()->SetConnectionCount(0);
754 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
755
756 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
757 EXPECT_EQ(1, connection_state_signal_count_);
Jonas Olsson635474e2018-10-18 15:58:17 +0200758 EXPECT_EQ(PeerConnectionInterface::kIceConnectionFailed,
759 ice_connection_state_);
760 EXPECT_EQ(1, ice_connection_state_signal_count_);
761 EXPECT_EQ(PeerConnectionInterface::PeerConnectionState::kFailed,
762 combined_connection_state_);
763 EXPECT_EQ(1, combined_connection_state_signal_count_);
Zhi Huange818b6e2018-02-22 15:26:27 -0800764
Jonas Olsson635474e2018-10-18 15:58:17 +0200765 fake_audio_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
766 fake_video_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
Zhi Huange818b6e2018-02-22 15:26:27 -0800767 // Set the connection count to be 2 and the cricket::FakeIceTransport will set
768 // the transport state to be STATE_CONNECTING.
769 fake_video_dtls->fake_ice_transport()->SetConnectionCount(2);
770 fake_video_dtls->SetWritable(true);
771 EXPECT_EQ_WAIT(cricket::kIceConnectionConnected, connection_state_, kTimeout);
772 EXPECT_EQ(2, connection_state_signal_count_);
Jonas Olsson635474e2018-10-18 15:58:17 +0200773 EXPECT_EQ(PeerConnectionInterface::kIceConnectionConnected,
774 ice_connection_state_);
775 EXPECT_EQ(2, ice_connection_state_signal_count_);
776 EXPECT_EQ(PeerConnectionInterface::PeerConnectionState::kConnected,
777 combined_connection_state_);
778 EXPECT_EQ(2, combined_connection_state_signal_count_);
Zhi Huange818b6e2018-02-22 15:26:27 -0800779}
780
781TEST_F(JsepTransportControllerTest, SignalConnectionStateComplete) {
782 CreateJsepTransportController(JsepTransportController::Config());
783 auto description = CreateSessionDescriptionWithoutBundle();
784 EXPECT_TRUE(transport_controller_
785 ->SetLocalDescription(SdpType::kOffer, description.get())
786 .ok());
787
788 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
789 transport_controller_->GetDtlsTransport(kAudioMid1));
790 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
791 transport_controller_->GetDtlsTransport(kVideoMid1));
792
793 // First, have one transport connect, and another fail, to ensure that
794 // the first transport connecting didn't trigger a "connected" state signal.
795 // We should only get a signal when all are connected.
796 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
797 fake_audio_dtls->SetWritable(true);
798 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
799 // Decrease the number of the connection to trigger the signal.
800 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
801 fake_video_dtls->fake_ice_transport()->SetConnectionCount(0);
802 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
803
804 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
805 EXPECT_EQ(1, connection_state_signal_count_);
Jonas Olsson635474e2018-10-18 15:58:17 +0200806 EXPECT_EQ(PeerConnectionInterface::kIceConnectionFailed,
807 ice_connection_state_);
808 EXPECT_EQ(1, ice_connection_state_signal_count_);
809 EXPECT_EQ(PeerConnectionInterface::PeerConnectionState::kFailed,
810 combined_connection_state_);
811 EXPECT_EQ(1, combined_connection_state_signal_count_);
Zhi Huange818b6e2018-02-22 15:26:27 -0800812
Jonas Olsson635474e2018-10-18 15:58:17 +0200813 fake_audio_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
814 fake_video_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
Zhi Huange818b6e2018-02-22 15:26:27 -0800815 // Set the connection count to be 1 and the cricket::FakeIceTransport will set
816 // the transport state to be STATE_COMPLETED.
817 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
818 fake_video_dtls->SetWritable(true);
819 EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
820 EXPECT_EQ(2, connection_state_signal_count_);
Jonas Olsson635474e2018-10-18 15:58:17 +0200821 EXPECT_EQ(PeerConnectionInterface::kIceConnectionCompleted,
822 ice_connection_state_);
823 EXPECT_EQ(2, ice_connection_state_signal_count_);
824 EXPECT_EQ(PeerConnectionInterface::PeerConnectionState::kConnected,
825 combined_connection_state_);
826 EXPECT_EQ(2, combined_connection_state_signal_count_);
Zhi Huange818b6e2018-02-22 15:26:27 -0800827}
828
829TEST_F(JsepTransportControllerTest, SignalIceGatheringStateGathering) {
830 CreateJsepTransportController(JsepTransportController::Config());
831 auto description = CreateSessionDescriptionWithoutBundle();
832 EXPECT_TRUE(transport_controller_
833 ->SetLocalDescription(SdpType::kOffer, description.get())
834 .ok());
835
836 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
837 transport_controller_->GetDtlsTransport(kAudioMid1));
838 fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
839 // Should be in the gathering state as soon as any transport starts gathering.
840 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
841 EXPECT_EQ(1, gathering_state_signal_count_);
842}
843
844TEST_F(JsepTransportControllerTest, SignalIceGatheringStateComplete) {
845 CreateJsepTransportController(JsepTransportController::Config());
846 auto description = CreateSessionDescriptionWithoutBundle();
847 EXPECT_TRUE(transport_controller_
848 ->SetLocalDescription(SdpType::kOffer, description.get())
849 .ok());
850
851 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
852 transport_controller_->GetDtlsTransport(kAudioMid1));
853 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
854 transport_controller_->GetDtlsTransport(kVideoMid1));
855
856 fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
857 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
858 EXPECT_EQ(1, gathering_state_signal_count_);
859
860 // Have one transport finish gathering, to make sure gathering
861 // completion wasn't signalled if only one transport finished gathering.
862 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
863 EXPECT_EQ(1, gathering_state_signal_count_);
864
865 fake_video_dtls->fake_ice_transport()->MaybeStartGathering();
866 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
867 EXPECT_EQ(1, gathering_state_signal_count_);
868
869 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
870 EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
871 EXPECT_EQ(2, gathering_state_signal_count_);
872}
873
874// Test that when the last transport that hasn't finished connecting and/or
875// gathering is destroyed, the aggregate state jumps to "completed". This can
876// happen if, for example, we have an audio and video transport, the audio
877// transport completes, then we start bundling video on the audio transport.
878TEST_F(JsepTransportControllerTest,
879 SignalingWhenLastIncompleteTransportDestroyed) {
880 CreateJsepTransportController(JsepTransportController::Config());
881 auto description = CreateSessionDescriptionWithBundleGroup();
882 EXPECT_TRUE(transport_controller_
883 ->SetLocalDescription(SdpType::kOffer, description.get())
884 .ok());
885
886 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
887 transport_controller_->GetDtlsTransport(kAudioMid1));
888 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
889 transport_controller_->GetDtlsTransport(kVideoMid1));
890 EXPECT_NE(fake_audio_dtls, fake_video_dtls);
891
892 fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
893 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
894 EXPECT_EQ(1, gathering_state_signal_count_);
895
896 // Let the audio transport complete.
897 fake_audio_dtls->SetWritable(true);
898 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
899 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
Jonas Olsson635474e2018-10-18 15:58:17 +0200900 fake_audio_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
Zhi Huange818b6e2018-02-22 15:26:27 -0800901 EXPECT_EQ(1, gathering_state_signal_count_);
902
903 // Set the remote description and enable the bundle.
904 EXPECT_TRUE(transport_controller_
905 ->SetRemoteDescription(SdpType::kAnswer, description.get())
906 .ok());
907 // The BUNDLE should be enabled, the incomplete video transport should be
908 // deleted and the states shoud be updated.
909 fake_video_dtls = static_cast<FakeDtlsTransport*>(
910 transport_controller_->GetDtlsTransport(kVideoMid1));
911 EXPECT_EQ(fake_audio_dtls, fake_video_dtls);
912 EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
Jonas Olsson635474e2018-10-18 15:58:17 +0200913 EXPECT_EQ(PeerConnectionInterface::kIceConnectionCompleted,
914 ice_connection_state_);
915 EXPECT_EQ(PeerConnectionInterface::PeerConnectionState::kConnected,
916 combined_connection_state_);
Zhi Huange818b6e2018-02-22 15:26:27 -0800917 EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
918 EXPECT_EQ(2, gathering_state_signal_count_);
919}
920
921TEST_F(JsepTransportControllerTest, SignalCandidatesGathered) {
922 CreateJsepTransportController(JsepTransportController::Config());
923 auto description = CreateSessionDescriptionWithBundleGroup();
924 EXPECT_TRUE(transport_controller_
925 ->SetLocalDescription(SdpType::kOffer, description.get())
926 .ok());
927 transport_controller_->MaybeStartGathering();
928
929 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
930 transport_controller_->GetDtlsTransport(kAudioMid1));
931 fake_audio_dtls->fake_ice_transport()->SignalCandidateGathered(
932 fake_audio_dtls->fake_ice_transport(), CreateCandidate(kAudioMid1, 1));
933 EXPECT_EQ_WAIT(1, candidates_signal_count_, kTimeout);
934 EXPECT_EQ(1u, candidates_[kAudioMid1].size());
935}
936
937TEST_F(JsepTransportControllerTest, IceSignalingOccursOnSignalingThread) {
938 network_thread_ = rtc::Thread::CreateWithSocketServer();
939 network_thread_->Start();
940 CreateJsepTransportController(JsepTransportController::Config(),
941 signaling_thread_, network_thread_.get(),
942 /*PortAllocator=*/nullptr);
943 CreateLocalDescriptionAndCompleteConnectionOnNetworkThread();
944
945 // connecting --> connected --> completed
946 EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
947 EXPECT_EQ(2, connection_state_signal_count_);
948
949 // new --> gathering --> complete
950 EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
951 EXPECT_EQ(2, gathering_state_signal_count_);
952
953 EXPECT_EQ_WAIT(1u, candidates_[kAudioMid1].size(), kTimeout);
954 EXPECT_EQ_WAIT(1u, candidates_[kVideoMid1].size(), kTimeout);
955 EXPECT_EQ(2, candidates_signal_count_);
956
957 EXPECT_TRUE(!signaled_on_non_signaling_thread_);
958}
959
960// Older versions of Chrome expect the ICE role to be re-determined when an
961// ICE restart occurs, and also don't perform conflict resolution correctly,
962// so for now we can't safely stop doing this.
963// See: https://bugs.chromium.org/p/chromium/issues/detail?id=628676
964// TODO(deadbeef): Remove this when these old versions of Chrome reach a low
965// enough population.
966TEST_F(JsepTransportControllerTest, IceRoleRedeterminedOnIceRestartByDefault) {
967 CreateJsepTransportController(JsepTransportController::Config());
968 // Let the |transport_controller_| be the controlled side initially.
Karl Wiberg918f50c2018-07-05 11:40:33 +0200969 auto remote_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800970 AddAudioSection(remote_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
971 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
972 nullptr);
Karl Wiberg918f50c2018-07-05 11:40:33 +0200973 auto local_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800974 AddAudioSection(local_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
975 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
976 nullptr);
977
978 EXPECT_TRUE(transport_controller_
979 ->SetRemoteDescription(SdpType::kOffer, remote_offer.get())
980 .ok());
981 EXPECT_TRUE(transport_controller_
982 ->SetLocalDescription(SdpType::kAnswer, local_answer.get())
983 .ok());
984
985 auto fake_dtls = static_cast<FakeDtlsTransport*>(
986 transport_controller_->GetDtlsTransport(kAudioMid1));
987 EXPECT_EQ(cricket::ICEROLE_CONTROLLED,
988 fake_dtls->fake_ice_transport()->GetIceRole());
989
990 // New offer will trigger the ICE restart.
Karl Wiberg918f50c2018-07-05 11:40:33 +0200991 auto restart_local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800992 AddAudioSection(restart_local_offer.get(), kAudioMid1, kIceUfrag3, kIcePwd3,
993 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
994 nullptr);
995 EXPECT_TRUE(
996 transport_controller_
997 ->SetLocalDescription(SdpType::kOffer, restart_local_offer.get())
998 .ok());
999 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
1000 fake_dtls->fake_ice_transport()->GetIceRole());
1001}
1002
1003// Test that if the TransportController was created with the
1004// |redetermine_role_on_ice_restart| parameter set to false, the role is *not*
1005// redetermined on an ICE restart.
1006TEST_F(JsepTransportControllerTest, IceRoleNotRedetermined) {
1007 JsepTransportController::Config config;
1008 config.redetermine_role_on_ice_restart = false;
1009
1010 CreateJsepTransportController(config);
1011 // Let the |transport_controller_| be the controlled side initially.
Karl Wiberg918f50c2018-07-05 11:40:33 +02001012 auto remote_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001013 AddAudioSection(remote_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1014 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1015 nullptr);
Karl Wiberg918f50c2018-07-05 11:40:33 +02001016 auto local_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001017 AddAudioSection(local_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1018 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1019 nullptr);
1020
1021 EXPECT_TRUE(transport_controller_
1022 ->SetRemoteDescription(SdpType::kOffer, remote_offer.get())
1023 .ok());
1024 EXPECT_TRUE(transport_controller_
1025 ->SetLocalDescription(SdpType::kAnswer, local_answer.get())
1026 .ok());
1027
1028 auto fake_dtls = static_cast<FakeDtlsTransport*>(
1029 transport_controller_->GetDtlsTransport(kAudioMid1));
1030 EXPECT_EQ(cricket::ICEROLE_CONTROLLED,
1031 fake_dtls->fake_ice_transport()->GetIceRole());
1032
1033 // New offer will trigger the ICE restart.
Karl Wiberg918f50c2018-07-05 11:40:33 +02001034 auto restart_local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001035 AddAudioSection(restart_local_offer.get(), kAudioMid1, kIceUfrag3, kIcePwd3,
1036 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1037 nullptr);
1038 EXPECT_TRUE(
1039 transport_controller_
1040 ->SetLocalDescription(SdpType::kOffer, restart_local_offer.get())
1041 .ok());
1042 EXPECT_EQ(cricket::ICEROLE_CONTROLLED,
1043 fake_dtls->fake_ice_transport()->GetIceRole());
1044}
1045
1046// Tests ICE-Lite mode in remote answer.
1047TEST_F(JsepTransportControllerTest, SetIceRoleWhenIceLiteInRemoteAnswer) {
1048 CreateJsepTransportController(JsepTransportController::Config());
Karl Wiberg918f50c2018-07-05 11:40:33 +02001049 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001050 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1051 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1052 nullptr);
1053 EXPECT_TRUE(transport_controller_
1054 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1055 .ok());
1056 auto fake_dtls = static_cast<FakeDtlsTransport*>(
1057 transport_controller_->GetDtlsTransport(kAudioMid1));
1058 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
1059 fake_dtls->fake_ice_transport()->GetIceRole());
1060 EXPECT_EQ(cricket::ICEMODE_FULL,
1061 fake_dtls->fake_ice_transport()->remote_ice_mode());
1062
Karl Wiberg918f50c2018-07-05 11:40:33 +02001063 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001064 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1065 cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_PASSIVE,
1066 nullptr);
1067 EXPECT_TRUE(transport_controller_
1068 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1069 .ok());
1070 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
1071 fake_dtls->fake_ice_transport()->GetIceRole());
1072 EXPECT_EQ(cricket::ICEMODE_LITE,
1073 fake_dtls->fake_ice_transport()->remote_ice_mode());
1074}
1075
1076// Tests that the ICE role remains "controlling" if a subsequent offer that
1077// does an ICE restart is received from an ICE lite endpoint. Regression test
1078// for: https://crbug.com/710760
1079TEST_F(JsepTransportControllerTest,
1080 IceRoleIsControllingAfterIceRestartFromIceLiteEndpoint) {
1081 CreateJsepTransportController(JsepTransportController::Config());
Karl Wiberg918f50c2018-07-05 11:40:33 +02001082 auto remote_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001083 AddAudioSection(remote_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1084 cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_ACTPASS,
1085 nullptr);
Karl Wiberg918f50c2018-07-05 11:40:33 +02001086 auto local_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001087 AddAudioSection(local_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1088 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1089 nullptr);
1090 // Initial Offer/Answer exchange. If the remote offerer is ICE-Lite, then the
1091 // local side is the controlling.
1092 EXPECT_TRUE(transport_controller_
1093 ->SetRemoteDescription(SdpType::kOffer, remote_offer.get())
1094 .ok());
1095 EXPECT_TRUE(transport_controller_
1096 ->SetLocalDescription(SdpType::kAnswer, local_answer.get())
1097 .ok());
1098 auto fake_dtls = static_cast<FakeDtlsTransport*>(
1099 transport_controller_->GetDtlsTransport(kAudioMid1));
1100 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
1101 fake_dtls->fake_ice_transport()->GetIceRole());
1102
1103 // In the subsequence remote offer triggers an ICE restart.
Karl Wiberg918f50c2018-07-05 11:40:33 +02001104 auto remote_offer2 = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001105 AddAudioSection(remote_offer2.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1106 cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_ACTPASS,
1107 nullptr);
Karl Wiberg918f50c2018-07-05 11:40:33 +02001108 auto local_answer2 = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001109 AddAudioSection(local_answer2.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1110 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1111 nullptr);
1112 EXPECT_TRUE(transport_controller_
1113 ->SetRemoteDescription(SdpType::kOffer, remote_offer2.get())
1114 .ok());
1115 EXPECT_TRUE(transport_controller_
1116 ->SetLocalDescription(SdpType::kAnswer, local_answer2.get())
1117 .ok());
1118 fake_dtls = static_cast<FakeDtlsTransport*>(
1119 transport_controller_->GetDtlsTransport(kAudioMid1));
1120 // The local side is still the controlling role since the remote side is using
1121 // ICE-Lite.
1122 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
1123 fake_dtls->fake_ice_transport()->GetIceRole());
1124}
1125
1126// Tests that the SDP has more than one audio/video m= sections.
1127TEST_F(JsepTransportControllerTest, MultipleMediaSectionsOfSameTypeWithBundle) {
1128 CreateJsepTransportController(JsepTransportController::Config());
1129 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1130 bundle_group.AddContentName(kAudioMid1);
1131 bundle_group.AddContentName(kAudioMid2);
1132 bundle_group.AddContentName(kVideoMid1);
1133 bundle_group.AddContentName(kDataMid1);
1134
Karl Wiberg918f50c2018-07-05 11:40:33 +02001135 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001136 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1137 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1138 nullptr);
1139 AddAudioSection(local_offer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
1140 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1141 nullptr);
1142 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1143 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1144 nullptr);
1145 AddDataSection(local_offer.get(), kDataMid1,
1146 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
1147 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1148 nullptr);
1149
Karl Wiberg918f50c2018-07-05 11:40:33 +02001150 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001151 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1152 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1153 nullptr);
1154 AddAudioSection(remote_answer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
1155 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1156 nullptr);
1157 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1158 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1159 nullptr);
1160 AddDataSection(remote_answer.get(), kDataMid1,
1161 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
1162 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1163 nullptr);
1164
1165 local_offer->AddGroup(bundle_group);
1166 remote_answer->AddGroup(bundle_group);
1167
1168 EXPECT_TRUE(transport_controller_
1169 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1170 .ok());
1171 EXPECT_TRUE(transport_controller_
1172 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1173 .ok());
1174 // Verify that all the sections are bundled on kAudio1.
1175 auto transport1 = transport_controller_->GetRtpTransport(kAudioMid1);
1176 auto transport2 = transport_controller_->GetRtpTransport(kAudioMid2);
1177 auto transport3 = transport_controller_->GetRtpTransport(kVideoMid1);
1178 auto transport4 = transport_controller_->GetRtpTransport(kDataMid1);
1179 EXPECT_EQ(transport1, transport2);
1180 EXPECT_EQ(transport1, transport3);
1181 EXPECT_EQ(transport1, transport4);
1182
1183 // Verify the OnRtpTransport/DtlsTransportChanged signals are fired correctly.
1184 auto it = changed_rtp_transport_by_mid_.find(kAudioMid2);
1185 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1186 EXPECT_EQ(transport1, it->second);
1187 it = changed_rtp_transport_by_mid_.find(kAudioMid2);
1188 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1189 EXPECT_EQ(transport1, it->second);
1190 it = changed_rtp_transport_by_mid_.find(kVideoMid1);
1191 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1192 EXPECT_EQ(transport1, it->second);
1193 // Verify the DtlsTransport for the SCTP data channel is reset correctly.
1194 auto it2 = changed_dtls_transport_by_mid_.find(kDataMid1);
1195 ASSERT_TRUE(it2 != changed_dtls_transport_by_mid_.end());
1196 EXPECT_EQ(transport1->rtp_packet_transport(), it2->second);
1197}
1198
1199// Tests that only a subset of all the m= sections are bundled.
1200TEST_F(JsepTransportControllerTest, BundleSubsetOfMediaSections) {
1201 CreateJsepTransportController(JsepTransportController::Config());
1202 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1203 bundle_group.AddContentName(kAudioMid1);
1204 bundle_group.AddContentName(kVideoMid1);
1205
Karl Wiberg918f50c2018-07-05 11:40:33 +02001206 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001207 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1208 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1209 nullptr);
1210 AddAudioSection(local_offer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
1211 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1212 nullptr);
1213 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1214 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1215 nullptr);
1216
Karl Wiberg918f50c2018-07-05 11:40:33 +02001217 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001218 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1219 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1220 nullptr);
1221 AddAudioSection(remote_answer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
1222 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1223 nullptr);
1224 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1225 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1226 nullptr);
1227
1228 local_offer->AddGroup(bundle_group);
1229 remote_answer->AddGroup(bundle_group);
1230 EXPECT_TRUE(transport_controller_
1231 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1232 .ok());
1233 EXPECT_TRUE(transport_controller_
1234 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1235 .ok());
1236
1237 // Verifiy that only |kAudio1| and |kVideo1| are bundled.
1238 auto transport1 = transport_controller_->GetRtpTransport(kAudioMid1);
1239 auto transport2 = transport_controller_->GetRtpTransport(kAudioMid2);
1240 auto transport3 = transport_controller_->GetRtpTransport(kVideoMid1);
1241 EXPECT_NE(transport1, transport2);
1242 EXPECT_EQ(transport1, transport3);
1243
1244 auto it = changed_rtp_transport_by_mid_.find(kVideoMid1);
1245 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1246 EXPECT_EQ(transport1, it->second);
1247 it = changed_rtp_transport_by_mid_.find(kAudioMid2);
Zhi Huangd2248f82018-04-10 14:41:03 -07001248 EXPECT_TRUE(transport2 == it->second);
Zhi Huange818b6e2018-02-22 15:26:27 -08001249}
1250
1251// Tests that the initial offer/answer only have data section and audio/video
1252// sections are added in the subsequent offer.
1253TEST_F(JsepTransportControllerTest, BundleOnDataSectionInSubsequentOffer) {
1254 CreateJsepTransportController(JsepTransportController::Config());
1255 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1256 bundle_group.AddContentName(kDataMid1);
1257
Karl Wiberg918f50c2018-07-05 11:40:33 +02001258 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001259 AddDataSection(local_offer.get(), kDataMid1,
1260 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
1261 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1262 nullptr);
Karl Wiberg918f50c2018-07-05 11:40:33 +02001263 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001264 AddDataSection(remote_answer.get(), kDataMid1,
1265 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
1266 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1267 nullptr);
1268 local_offer->AddGroup(bundle_group);
1269 remote_answer->AddGroup(bundle_group);
1270
1271 EXPECT_TRUE(transport_controller_
1272 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1273 .ok());
1274 EXPECT_TRUE(transport_controller_
1275 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1276 .ok());
1277 auto data_transport = transport_controller_->GetRtpTransport(kDataMid1);
1278
1279 // Add audio/video sections in subsequent offer.
1280 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1281 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1282 nullptr);
1283 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1284 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1285 nullptr);
1286 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1287 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1288 nullptr);
1289 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1290 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1291 nullptr);
1292
1293 // Reset the bundle group and do another offer/answer exchange.
1294 bundle_group.AddContentName(kAudioMid1);
1295 bundle_group.AddContentName(kVideoMid1);
1296 local_offer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1297 remote_answer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1298 local_offer->AddGroup(bundle_group);
1299 remote_answer->AddGroup(bundle_group);
1300
1301 EXPECT_TRUE(transport_controller_
1302 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1303 .ok());
1304 EXPECT_TRUE(transport_controller_
1305 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1306 .ok());
1307
1308 auto audio_transport = transport_controller_->GetRtpTransport(kAudioMid1);
1309 auto video_transport = transport_controller_->GetRtpTransport(kVideoMid1);
1310 EXPECT_EQ(data_transport, audio_transport);
1311 EXPECT_EQ(data_transport, video_transport);
1312}
1313
1314TEST_F(JsepTransportControllerTest, VideoDataRejectedInAnswer) {
1315 CreateJsepTransportController(JsepTransportController::Config());
1316 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1317 bundle_group.AddContentName(kAudioMid1);
1318 bundle_group.AddContentName(kVideoMid1);
1319 bundle_group.AddContentName(kDataMid1);
1320
Karl Wiberg918f50c2018-07-05 11:40:33 +02001321 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001322 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1323 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1324 nullptr);
1325 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1326 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1327 nullptr);
1328 AddDataSection(local_offer.get(), kDataMid1,
1329 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1330 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1331 nullptr);
1332
Karl Wiberg918f50c2018-07-05 11:40:33 +02001333 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001334 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1335 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1336 nullptr);
1337 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1338 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1339 nullptr);
1340 AddDataSection(remote_answer.get(), kDataMid1,
1341 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1342 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1343 nullptr);
1344 // Reject video and data section.
1345 remote_answer->contents()[1].rejected = true;
1346 remote_answer->contents()[2].rejected = true;
1347
1348 local_offer->AddGroup(bundle_group);
1349 remote_answer->AddGroup(bundle_group);
1350
1351 EXPECT_TRUE(transport_controller_
1352 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1353 .ok());
1354 EXPECT_TRUE(transport_controller_
1355 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1356 .ok());
1357
1358 // Verify the RtpTransport/DtlsTransport is destroyed correctly.
1359 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kVideoMid1));
1360 EXPECT_EQ(nullptr, transport_controller_->GetDtlsTransport(kDataMid1));
1361 // Verify the signals are fired correctly.
1362 auto it = changed_rtp_transport_by_mid_.find(kVideoMid1);
1363 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1364 EXPECT_EQ(nullptr, it->second);
1365 auto it2 = changed_dtls_transport_by_mid_.find(kDataMid1);
1366 ASSERT_TRUE(it2 != changed_dtls_transport_by_mid_.end());
1367 EXPECT_EQ(nullptr, it2->second);
1368}
1369
1370// Tests that changing the bundled MID in subsequent offer/answer exchange is
1371// not supported.
1372// TODO(bugs.webrtc.org/6704): Change this test to expect success once issue is
1373// fixed
1374TEST_F(JsepTransportControllerTest, ChangeBundledMidNotSupported) {
1375 CreateJsepTransportController(JsepTransportController::Config());
1376 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1377 bundle_group.AddContentName(kAudioMid1);
1378 bundle_group.AddContentName(kVideoMid1);
1379
Karl Wiberg918f50c2018-07-05 11:40:33 +02001380 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001381 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1382 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1383 nullptr);
1384 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1385 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1386 nullptr);
1387
Karl Wiberg918f50c2018-07-05 11:40:33 +02001388 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001389 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1390 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1391 nullptr);
1392 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1393 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1394 nullptr);
1395
1396 local_offer->AddGroup(bundle_group);
1397 remote_answer->AddGroup(bundle_group);
1398 EXPECT_TRUE(transport_controller_
1399 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1400 .ok());
1401 EXPECT_TRUE(transport_controller_
1402 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1403 .ok());
1404 EXPECT_EQ(transport_controller_->GetRtpTransport(kAudioMid1),
1405 transport_controller_->GetRtpTransport(kVideoMid1));
1406
1407 // Reorder the bundle group.
1408 EXPECT_TRUE(bundle_group.RemoveContentName(kAudioMid1));
1409 bundle_group.AddContentName(kAudioMid1);
1410 // The answerer uses the new bundle group and now the bundle mid is changed to
1411 // |kVideo1|.
1412 remote_answer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1413 remote_answer->AddGroup(bundle_group);
1414 EXPECT_TRUE(transport_controller_
1415 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1416 .ok());
1417 EXPECT_FALSE(transport_controller_
1418 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1419 .ok());
1420}
Zhi Huange830e682018-03-30 10:48:35 -07001421// Test that rejecting only the first m= section of a BUNDLE group is treated as
1422// an error, but rejecting all of them works as expected.
1423TEST_F(JsepTransportControllerTest, RejectFirstContentInBundleGroup) {
1424 CreateJsepTransportController(JsepTransportController::Config());
1425 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1426 bundle_group.AddContentName(kAudioMid1);
1427 bundle_group.AddContentName(kVideoMid1);
1428 bundle_group.AddContentName(kDataMid1);
1429
Karl Wiberg918f50c2018-07-05 11:40:33 +02001430 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange830e682018-03-30 10:48:35 -07001431 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1432 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1433 nullptr);
1434 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1435 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1436 nullptr);
1437 AddDataSection(local_offer.get(), kDataMid1,
1438 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1439 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1440 nullptr);
1441
Karl Wiberg918f50c2018-07-05 11:40:33 +02001442 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange830e682018-03-30 10:48:35 -07001443 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1444 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1445 nullptr);
1446 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1447 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1448 nullptr);
1449 AddDataSection(remote_answer.get(), kDataMid1,
1450 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1451 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1452 nullptr);
1453 // Reject audio content in answer.
1454 remote_answer->contents()[0].rejected = true;
1455
1456 local_offer->AddGroup(bundle_group);
1457 remote_answer->AddGroup(bundle_group);
1458
1459 EXPECT_TRUE(transport_controller_
1460 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1461 .ok());
1462 EXPECT_FALSE(transport_controller_
1463 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1464 .ok());
1465
1466 // Reject all the contents.
1467 remote_answer->contents()[1].rejected = true;
1468 remote_answer->contents()[2].rejected = true;
1469 EXPECT_TRUE(transport_controller_
1470 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1471 .ok());
1472 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kAudioMid1));
1473 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kVideoMid1));
1474 EXPECT_EQ(nullptr, transport_controller_->GetDtlsTransport(kDataMid1));
1475}
1476
1477// Tests that applying non-RTCP-mux offer would fail when kRtcpMuxPolicyRequire
1478// is used.
1479TEST_F(JsepTransportControllerTest, ApplyNonRtcpMuxOfferWhenMuxingRequired) {
1480 JsepTransportController::Config config;
1481 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
1482 CreateJsepTransportController(config);
Karl Wiberg918f50c2018-07-05 11:40:33 +02001483 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange830e682018-03-30 10:48:35 -07001484 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1485 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1486 nullptr);
1487
1488 local_offer->contents()[0].media_description()->set_rtcp_mux(false);
1489 // Applying a non-RTCP-mux offer is expected to fail.
1490 EXPECT_FALSE(transport_controller_
1491 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1492 .ok());
1493}
1494
1495// Tests that applying non-RTCP-mux answer would fail when kRtcpMuxPolicyRequire
1496// is used.
1497TEST_F(JsepTransportControllerTest, ApplyNonRtcpMuxAnswerWhenMuxingRequired) {
1498 JsepTransportController::Config config;
1499 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
1500 CreateJsepTransportController(config);
Karl Wiberg918f50c2018-07-05 11:40:33 +02001501 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange830e682018-03-30 10:48:35 -07001502 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1503 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1504 nullptr);
1505 EXPECT_TRUE(transport_controller_
1506 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1507 .ok());
1508
Karl Wiberg918f50c2018-07-05 11:40:33 +02001509 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange830e682018-03-30 10:48:35 -07001510 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1511 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1512 nullptr);
1513 // Applying a non-RTCP-mux answer is expected to fail.
1514 remote_answer->contents()[0].media_description()->set_rtcp_mux(false);
1515 EXPECT_FALSE(transport_controller_
1516 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1517 .ok());
1518}
Zhi Huange818b6e2018-02-22 15:26:27 -08001519
Zhi Huangd2248f82018-04-10 14:41:03 -07001520// This tests that the BUNDLE group in answer should be a subset of the offered
1521// group.
1522TEST_F(JsepTransportControllerTest,
1523 AddContentToBundleGroupInAnswerNotSupported) {
1524 CreateJsepTransportController(JsepTransportController::Config());
1525 auto local_offer = CreateSessionDescriptionWithoutBundle();
1526 auto remote_answer = CreateSessionDescriptionWithoutBundle();
1527
1528 cricket::ContentGroup offer_bundle_group(cricket::GROUP_TYPE_BUNDLE);
1529 offer_bundle_group.AddContentName(kAudioMid1);
1530 local_offer->AddGroup(offer_bundle_group);
1531
1532 cricket::ContentGroup answer_bundle_group(cricket::GROUP_TYPE_BUNDLE);
1533 answer_bundle_group.AddContentName(kAudioMid1);
1534 answer_bundle_group.AddContentName(kVideoMid1);
1535 remote_answer->AddGroup(answer_bundle_group);
1536 EXPECT_TRUE(transport_controller_
1537 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1538 .ok());
1539 EXPECT_FALSE(transport_controller_
1540 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1541 .ok());
1542}
1543
1544// This tests that the BUNDLE group with non-existing MID should be rejectd.
1545TEST_F(JsepTransportControllerTest, RejectBundleGroupWithNonExistingMid) {
1546 CreateJsepTransportController(JsepTransportController::Config());
1547 auto local_offer = CreateSessionDescriptionWithoutBundle();
1548 auto remote_answer = CreateSessionDescriptionWithoutBundle();
1549
1550 cricket::ContentGroup invalid_bundle_group(cricket::GROUP_TYPE_BUNDLE);
1551 // The BUNDLE group is invalid because there is no data section in the
1552 // description.
1553 invalid_bundle_group.AddContentName(kDataMid1);
1554 local_offer->AddGroup(invalid_bundle_group);
1555 remote_answer->AddGroup(invalid_bundle_group);
1556
1557 EXPECT_FALSE(transport_controller_
1558 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1559 .ok());
1560 EXPECT_FALSE(transport_controller_
1561 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1562 .ok());
1563}
1564
1565// This tests that an answer shouldn't be able to remove an m= section from an
1566// established group without rejecting it.
1567TEST_F(JsepTransportControllerTest, RemoveContentFromBundleGroup) {
1568 CreateJsepTransportController(JsepTransportController::Config());
1569
1570 auto local_offer = CreateSessionDescriptionWithBundleGroup();
1571 auto remote_answer = CreateSessionDescriptionWithBundleGroup();
1572 EXPECT_TRUE(transport_controller_
1573 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1574 .ok());
1575 EXPECT_TRUE(transport_controller_
1576 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1577 .ok());
1578
1579 // Do an re-offer/answer.
1580 EXPECT_TRUE(transport_controller_
1581 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1582 .ok());
1583 auto new_answer = CreateSessionDescriptionWithoutBundle();
1584 cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
1585 // The answer removes video from the BUNDLE group without rejecting it is
1586 // invalid.
1587 new_bundle_group.AddContentName(kAudioMid1);
1588 new_answer->AddGroup(new_bundle_group);
1589
1590 // Applying invalid answer is expected to fail.
1591 EXPECT_FALSE(transport_controller_
1592 ->SetRemoteDescription(SdpType::kAnswer, new_answer.get())
1593 .ok());
1594
1595 // Rejected the video content.
1596 auto video_content = new_answer->GetContentByName(kVideoMid1);
1597 ASSERT_TRUE(video_content);
1598 video_content->rejected = true;
1599 EXPECT_TRUE(transport_controller_
1600 ->SetRemoteDescription(SdpType::kAnswer, new_answer.get())
1601 .ok());
1602}
1603
Zhi Huange818b6e2018-02-22 15:26:27 -08001604} // namespace webrtc