blob: ba9b72d208e237e783c8ea87451154695d28a19c [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
44class FakeTransportFactory : public cricket::TransportFactoryInterface {
45 public:
46 std::unique_ptr<cricket::IceTransportInternal> CreateIceTransport(
47 const std::string& transport_name,
48 int component) override {
Karl Wiberg918f50c2018-07-05 11:40:33 +020049 return absl::make_unique<cricket::FakeIceTransport>(transport_name,
50 component);
Zhi Huange818b6e2018-02-22 15:26:27 -080051 }
52
53 std::unique_ptr<cricket::DtlsTransportInternal> CreateDtlsTransport(
54 std::unique_ptr<cricket::IceTransportInternal> ice,
Benjamin Wrighta54daf12018-10-11 15:33:17 -070055 const webrtc::CryptoOptions& crypto_options) override {
Zhi Huange818b6e2018-02-22 15:26:27 -080056 std::unique_ptr<cricket::FakeIceTransport> fake_ice(
57 static_cast<cricket::FakeIceTransport*>(ice.release()));
Karl Wiberg918f50c2018-07-05 11:40:33 +020058 return absl::make_unique<FakeDtlsTransport>(std::move(fake_ice));
Zhi Huange818b6e2018-02-22 15:26:27 -080059 }
60};
61
Zhi Huang365381f2018-04-13 16:44:34 -070062class JsepTransportControllerTest : public JsepTransportController::Observer,
63 public testing::Test,
Zhi Huange818b6e2018-02-22 15:26:27 -080064 public sigslot::has_slots<> {
65 public:
66 JsepTransportControllerTest() : signaling_thread_(rtc::Thread::Current()) {
Karl Wiberg918f50c2018-07-05 11:40:33 +020067 fake_transport_factory_ = absl::make_unique<FakeTransportFactory>();
Zhi Huange818b6e2018-02-22 15:26:27 -080068 }
69
70 void CreateJsepTransportController(
71 JsepTransportController::Config config,
72 rtc::Thread* signaling_thread = rtc::Thread::Current(),
73 rtc::Thread* network_thread = rtc::Thread::Current(),
74 cricket::PortAllocator* port_allocator = nullptr) {
Zhi Huang365381f2018-04-13 16:44:34 -070075 config.transport_observer = this;
Zhi Huange818b6e2018-02-22 15:26:27 -080076 // The tests only works with |fake_transport_factory|;
77 config.external_transport_factory = fake_transport_factory_.get();
Zach Steine20867f2018-08-02 13:20:15 -070078 // TODO(zstein): Provide an AsyncResolverFactory once it is required.
Karl Wiberg918f50c2018-07-05 11:40:33 +020079 transport_controller_ = absl::make_unique<JsepTransportController>(
Zach Steine20867f2018-08-02 13:20:15 -070080 signaling_thread, network_thread, port_allocator, nullptr, config);
Zhi Huange818b6e2018-02-22 15:26:27 -080081 ConnectTransportControllerSignals();
82 }
83
84 void ConnectTransportControllerSignals() {
85 transport_controller_->SignalIceConnectionState.connect(
86 this, &JsepTransportControllerTest::OnConnectionState);
Jonas Olsson635474e2018-10-18 15:58:17 +020087 transport_controller_->SignalStandardizedIceConnectionState.connect(
88 this, &JsepTransportControllerTest::OnStandardizedIceConnectionState);
89 transport_controller_->SignalConnectionState.connect(
90 this, &JsepTransportControllerTest::OnCombinedConnectionState);
Zhi Huange818b6e2018-02-22 15:26:27 -080091 transport_controller_->SignalIceGatheringState.connect(
92 this, &JsepTransportControllerTest::OnGatheringState);
93 transport_controller_->SignalIceCandidatesGathered.connect(
94 this, &JsepTransportControllerTest::OnCandidatesGathered);
Zhi Huange818b6e2018-02-22 15:26:27 -080095 }
96
97 std::unique_ptr<cricket::SessionDescription>
98 CreateSessionDescriptionWithoutBundle() {
Karl Wiberg918f50c2018-07-05 11:40:33 +020099 auto description = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800100 AddAudioSection(description.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
101 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
102 nullptr);
103 AddVideoSection(description.get(), kVideoMid1, kIceUfrag1, kIcePwd1,
104 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
105 nullptr);
106 return description;
107 }
108
109 std::unique_ptr<cricket::SessionDescription>
110 CreateSessionDescriptionWithBundleGroup() {
111 auto description = CreateSessionDescriptionWithoutBundle();
112 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
113 bundle_group.AddContentName(kAudioMid1);
114 bundle_group.AddContentName(kVideoMid1);
115 description->AddGroup(bundle_group);
116
117 return description;
118 }
119
120 void AddAudioSection(cricket::SessionDescription* description,
121 const std::string& mid,
122 const std::string& ufrag,
123 const std::string& pwd,
124 cricket::IceMode ice_mode,
125 cricket::ConnectionRole conn_role,
126 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
127 std::unique_ptr<cricket::AudioContentDescription> audio(
128 new cricket::AudioContentDescription());
Zhi Huange830e682018-03-30 10:48:35 -0700129 // Set RTCP-mux to be true because the default policy is "mux required".
130 audio->set_rtcp_mux(true);
Zhi Huange818b6e2018-02-22 15:26:27 -0800131 description->AddContent(mid, cricket::MediaProtocolType::kRtp,
132 /*rejected=*/false, audio.release());
133 AddTransportInfo(description, mid, ufrag, pwd, ice_mode, conn_role, cert);
134 }
135
136 void AddVideoSection(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::VideoContentDescription> video(
144 new cricket::VideoContentDescription());
Zhi Huange830e682018-03-30 10:48:35 -0700145 // Set RTCP-mux to be true because the default policy is "mux required".
146 video->set_rtcp_mux(true);
Zhi Huange818b6e2018-02-22 15:26:27 -0800147 description->AddContent(mid, cricket::MediaProtocolType::kRtp,
148 /*rejected=*/false, video.release());
149 AddTransportInfo(description, mid, ufrag, pwd, ice_mode, conn_role, cert);
150 }
151
152 void AddDataSection(cricket::SessionDescription* description,
153 const std::string& mid,
154 cricket::MediaProtocolType protocol_type,
155 const std::string& ufrag,
156 const std::string& pwd,
157 cricket::IceMode ice_mode,
158 cricket::ConnectionRole conn_role,
159 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
160 std::unique_ptr<cricket::DataContentDescription> data(
161 new cricket::DataContentDescription());
Zhi Huange830e682018-03-30 10:48:35 -0700162 data->set_rtcp_mux(true);
Zhi Huange818b6e2018-02-22 15:26:27 -0800163 description->AddContent(mid, protocol_type,
164 /*rejected=*/false, data.release());
165 AddTransportInfo(description, mid, ufrag, pwd, ice_mode, conn_role, cert);
166 }
167
168 void AddTransportInfo(cricket::SessionDescription* description,
169 const std::string& mid,
170 const std::string& ufrag,
171 const std::string& pwd,
172 cricket::IceMode ice_mode,
173 cricket::ConnectionRole conn_role,
174 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
175 std::unique_ptr<rtc::SSLFingerprint> fingerprint;
176 if (cert) {
Steve Anton4905edb2018-10-15 19:27:44 -0700177 fingerprint = rtc::SSLFingerprint::CreateFromCertificate(*cert);
Zhi Huange818b6e2018-02-22 15:26:27 -0800178 }
179
180 cricket::TransportDescription transport_desc(std::vector<std::string>(),
181 ufrag, pwd, ice_mode,
182 conn_role, fingerprint.get());
183 description->AddTransportInfo(cricket::TransportInfo(mid, transport_desc));
184 }
185
186 cricket::IceConfig CreateIceConfig(
187 int receiving_timeout,
188 cricket::ContinualGatheringPolicy continual_gathering_policy) {
189 cricket::IceConfig config;
190 config.receiving_timeout = receiving_timeout;
191 config.continual_gathering_policy = continual_gathering_policy;
192 return config;
193 }
194
195 Candidate CreateCandidate(const std::string& transport_name, int component) {
196 Candidate c;
197 c.set_transport_name(transport_name);
198 c.set_address(rtc::SocketAddress("192.168.1.1", 8000));
199 c.set_component(component);
200 c.set_protocol(cricket::UDP_PROTOCOL_NAME);
201 c.set_priority(1);
202 return c;
203 }
204
205 void CreateLocalDescriptionAndCompleteConnectionOnNetworkThread() {
206 if (!network_thread_->IsCurrent()) {
207 network_thread_->Invoke<void>(RTC_FROM_HERE, [&] {
208 CreateLocalDescriptionAndCompleteConnectionOnNetworkThread();
209 });
210 return;
211 }
212
213 auto description = CreateSessionDescriptionWithBundleGroup();
214 EXPECT_TRUE(transport_controller_
215 ->SetLocalDescription(SdpType::kOffer, description.get())
216 .ok());
217
218 transport_controller_->MaybeStartGathering();
219 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
220 transport_controller_->GetDtlsTransport(kAudioMid1));
221 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
222 transport_controller_->GetDtlsTransport(kVideoMid1));
223 fake_audio_dtls->fake_ice_transport()->SignalCandidateGathered(
224 fake_audio_dtls->fake_ice_transport(),
225 CreateCandidate(kAudioMid1, /*component=*/1));
226 fake_video_dtls->fake_ice_transport()->SignalCandidateGathered(
227 fake_video_dtls->fake_ice_transport(),
228 CreateCandidate(kVideoMid1, /*component=*/1));
229 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
230 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
231 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(2);
232 fake_video_dtls->fake_ice_transport()->SetConnectionCount(2);
233 fake_audio_dtls->SetReceiving(true);
234 fake_video_dtls->SetReceiving(true);
235 fake_audio_dtls->SetWritable(true);
236 fake_video_dtls->SetWritable(true);
237 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
238 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
239 }
240
241 protected:
242 void OnConnectionState(cricket::IceConnectionState state) {
243 if (!signaling_thread_->IsCurrent()) {
244 signaled_on_non_signaling_thread_ = true;
245 }
246 connection_state_ = state;
247 ++connection_state_signal_count_;
248 }
249
Jonas Olsson635474e2018-10-18 15:58:17 +0200250 void OnStandardizedIceConnectionState(
251 PeerConnectionInterface::IceConnectionState state) {
252 if (!signaling_thread_->IsCurrent()) {
253 signaled_on_non_signaling_thread_ = true;
254 }
255 ice_connection_state_ = state;
256 ++ice_connection_state_signal_count_;
257 }
258
259 void OnCombinedConnectionState(
260 PeerConnectionInterface::PeerConnectionState state) {
261 if (!signaling_thread_->IsCurrent()) {
262 signaled_on_non_signaling_thread_ = true;
263 }
264 combined_connection_state_ = state;
265 ++combined_connection_state_signal_count_;
266 }
267
Zhi Huange818b6e2018-02-22 15:26:27 -0800268 void OnGatheringState(cricket::IceGatheringState state) {
269 if (!signaling_thread_->IsCurrent()) {
270 signaled_on_non_signaling_thread_ = true;
271 }
272 gathering_state_ = state;
273 ++gathering_state_signal_count_;
274 }
275
276 void OnCandidatesGathered(const std::string& transport_name,
277 const Candidates& candidates) {
278 if (!signaling_thread_->IsCurrent()) {
279 signaled_on_non_signaling_thread_ = true;
280 }
281 candidates_[transport_name].insert(candidates_[transport_name].end(),
282 candidates.begin(), candidates.end());
283 ++candidates_signal_count_;
284 }
285
Zhi Huang365381f2018-04-13 16:44:34 -0700286 // JsepTransportController::Observer overrides.
Taylor Brandstettercbaa2542018-04-16 16:42:14 -0700287 bool OnTransportChanged(
Zhi Huang365381f2018-04-13 16:44:34 -0700288 const std::string& mid,
Taylor Brandstettercbaa2542018-04-16 16:42:14 -0700289 RtpTransportInternal* rtp_transport,
Zhi Huang365381f2018-04-13 16:44:34 -0700290 cricket::DtlsTransportInternal* dtls_transport) override {
Taylor Brandstettercbaa2542018-04-16 16:42:14 -0700291 changed_rtp_transport_by_mid_[mid] = rtp_transport;
Zhi Huange818b6e2018-02-22 15:26:27 -0800292 changed_dtls_transport_by_mid_[mid] = dtls_transport;
Taylor Brandstettercbaa2542018-04-16 16:42:14 -0700293 return true;
Zhi Huange818b6e2018-02-22 15:26:27 -0800294 }
295
296 // Information received from signals from transport controller.
297 cricket::IceConnectionState connection_state_ =
298 cricket::kIceConnectionConnecting;
Jonas Olsson635474e2018-10-18 15:58:17 +0200299 PeerConnectionInterface::IceConnectionState ice_connection_state_ =
300 PeerConnectionInterface::kIceConnectionNew;
301 PeerConnectionInterface::PeerConnectionState combined_connection_state_ =
302 PeerConnectionInterface::PeerConnectionState::kNew;
Zhi Huange818b6e2018-02-22 15:26:27 -0800303 bool receiving_ = false;
304 cricket::IceGatheringState gathering_state_ = cricket::kIceGatheringNew;
305 // transport_name => candidates
306 std::map<std::string, Candidates> candidates_;
307 // Counts of each signal emitted.
308 int connection_state_signal_count_ = 0;
Jonas Olsson635474e2018-10-18 15:58:17 +0200309 int ice_connection_state_signal_count_ = 0;
310 int combined_connection_state_signal_count_ = 0;
Zhi Huange818b6e2018-02-22 15:26:27 -0800311 int receiving_signal_count_ = 0;
312 int gathering_state_signal_count_ = 0;
313 int candidates_signal_count_ = 0;
314
315 // |network_thread_| should be destroyed after |transport_controller_|
316 std::unique_ptr<rtc::Thread> network_thread_;
317 std::unique_ptr<JsepTransportController> transport_controller_;
318 std::unique_ptr<FakeTransportFactory> fake_transport_factory_;
319 rtc::Thread* const signaling_thread_ = nullptr;
320 bool signaled_on_non_signaling_thread_ = false;
321 // Used to verify the SignalRtpTransportChanged/SignalDtlsTransportChanged are
322 // signaled correctly.
323 std::map<std::string, RtpTransportInternal*> changed_rtp_transport_by_mid_;
324 std::map<std::string, cricket::DtlsTransportInternal*>
325 changed_dtls_transport_by_mid_;
326};
327
328TEST_F(JsepTransportControllerTest, GetRtpTransport) {
329 CreateJsepTransportController(JsepTransportController::Config());
330 auto description = CreateSessionDescriptionWithoutBundle();
331 EXPECT_TRUE(transport_controller_
332 ->SetLocalDescription(SdpType::kOffer, description.get())
333 .ok());
334 auto audio_rtp_transport = transport_controller_->GetRtpTransport(kAudioMid1);
335 auto video_rtp_transport = transport_controller_->GetRtpTransport(kVideoMid1);
336 EXPECT_NE(nullptr, audio_rtp_transport);
337 EXPECT_NE(nullptr, video_rtp_transport);
338 EXPECT_NE(audio_rtp_transport, video_rtp_transport);
339 // Return nullptr for non-existing ones.
340 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kAudioMid2));
341}
342
343TEST_F(JsepTransportControllerTest, GetDtlsTransport) {
344 JsepTransportController::Config config;
345 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
346 CreateJsepTransportController(config);
347 auto description = CreateSessionDescriptionWithoutBundle();
348 EXPECT_TRUE(transport_controller_
349 ->SetLocalDescription(SdpType::kOffer, description.get())
350 .ok());
351 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kAudioMid1));
352 EXPECT_NE(nullptr, transport_controller_->GetRtcpDtlsTransport(kAudioMid1));
353 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kVideoMid1));
354 EXPECT_NE(nullptr, transport_controller_->GetRtcpDtlsTransport(kVideoMid1));
355 // Return nullptr for non-existing ones.
356 EXPECT_EQ(nullptr, transport_controller_->GetDtlsTransport(kVideoMid2));
357 EXPECT_EQ(nullptr, transport_controller_->GetRtcpDtlsTransport(kVideoMid2));
358}
359
Zhi Huange830e682018-03-30 10:48:35 -0700360TEST_F(JsepTransportControllerTest, GetDtlsTransportWithRtcpMux) {
361 JsepTransportController::Config config;
362 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
363 CreateJsepTransportController(config);
364 auto description = CreateSessionDescriptionWithoutBundle();
365 EXPECT_TRUE(transport_controller_
366 ->SetLocalDescription(SdpType::kOffer, description.get())
367 .ok());
368 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kAudioMid1));
369 EXPECT_EQ(nullptr, transport_controller_->GetRtcpDtlsTransport(kAudioMid1));
370 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kVideoMid1));
371 EXPECT_EQ(nullptr, transport_controller_->GetRtcpDtlsTransport(kVideoMid1));
Anton Sukhanov7940da02018-10-10 10:34:49 -0700372 EXPECT_EQ(nullptr, transport_controller_->GetMediaTransport(kAudioMid1));
373}
374
375TEST_F(JsepTransportControllerTest, GetMediaTransportInCaller) {
376 FakeMediaTransportFactory fake_media_transport_factory;
377 JsepTransportController::Config config;
378
379 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
380 config.media_transport_factory = &fake_media_transport_factory;
381 CreateJsepTransportController(config);
382 auto description = CreateSessionDescriptionWithoutBundle();
383 EXPECT_TRUE(transport_controller_
384 ->SetLocalDescription(SdpType::kOffer, description.get())
385 .ok());
386
387 FakeMediaTransport* media_transport = static_cast<FakeMediaTransport*>(
388 transport_controller_->GetMediaTransport(kAudioMid1));
389
390 ASSERT_NE(nullptr, media_transport);
391
392 // After SetLocalDescription, media transport should be created as caller.
393 EXPECT_TRUE(media_transport->is_caller());
394
395 // Return nullptr for non-existing mids.
396 EXPECT_EQ(nullptr, transport_controller_->GetMediaTransport(kVideoMid2));
397}
398
399TEST_F(JsepTransportControllerTest, GetMediaTransportInCallee) {
400 FakeMediaTransportFactory fake_media_transport_factory;
401 JsepTransportController::Config config;
402
403 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
404 config.media_transport_factory = &fake_media_transport_factory;
405 CreateJsepTransportController(config);
406 auto description = CreateSessionDescriptionWithoutBundle();
407 EXPECT_TRUE(transport_controller_
408 ->SetRemoteDescription(SdpType::kOffer, description.get())
409 .ok());
410
411 FakeMediaTransport* media_transport = static_cast<FakeMediaTransport*>(
412 transport_controller_->GetMediaTransport(kAudioMid1));
413
414 ASSERT_NE(nullptr, media_transport);
415
416 // After SetRemoteDescription, media transport should be created as callee.
417 EXPECT_FALSE(media_transport->is_caller());
418
419 // Return nullptr for non-existing mids.
420 EXPECT_EQ(nullptr, transport_controller_->GetMediaTransport(kVideoMid2));
Zhi Huange830e682018-03-30 10:48:35 -0700421}
422
Zhi Huange818b6e2018-02-22 15:26:27 -0800423TEST_F(JsepTransportControllerTest, SetIceConfig) {
424 CreateJsepTransportController(JsepTransportController::Config());
425 auto description = CreateSessionDescriptionWithoutBundle();
426 EXPECT_TRUE(transport_controller_
427 ->SetLocalDescription(SdpType::kOffer, description.get())
428 .ok());
429
430 transport_controller_->SetIceConfig(
431 CreateIceConfig(kTimeout, cricket::GATHER_CONTINUALLY));
432 FakeDtlsTransport* fake_audio_dtls = static_cast<FakeDtlsTransport*>(
433 transport_controller_->GetDtlsTransport(kAudioMid1));
434 ASSERT_NE(nullptr, fake_audio_dtls);
435 EXPECT_EQ(kTimeout,
436 fake_audio_dtls->fake_ice_transport()->receiving_timeout());
437 EXPECT_TRUE(fake_audio_dtls->fake_ice_transport()->gather_continually());
438
439 // Test that value stored in controller is applied to new transports.
440 AddAudioSection(description.get(), kAudioMid2, kIceUfrag1, kIcePwd1,
441 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
442 nullptr);
443
444 EXPECT_TRUE(transport_controller_
445 ->SetLocalDescription(SdpType::kOffer, description.get())
446 .ok());
447 fake_audio_dtls = static_cast<FakeDtlsTransport*>(
448 transport_controller_->GetDtlsTransport(kAudioMid2));
449 ASSERT_NE(nullptr, fake_audio_dtls);
450 EXPECT_EQ(kTimeout,
451 fake_audio_dtls->fake_ice_transport()->receiving_timeout());
452 EXPECT_TRUE(fake_audio_dtls->fake_ice_transport()->gather_continually());
453}
454
455// Tests the getter and setter of the ICE restart flag.
456TEST_F(JsepTransportControllerTest, NeedIceRestart) {
457 CreateJsepTransportController(JsepTransportController::Config());
458 auto description = CreateSessionDescriptionWithoutBundle();
459 EXPECT_TRUE(transport_controller_
460 ->SetLocalDescription(SdpType::kOffer, description.get())
461 .ok());
462 EXPECT_TRUE(transport_controller_
463 ->SetRemoteDescription(SdpType::kAnswer, description.get())
464 .ok());
465
466 // Initially NeedsIceRestart should return false.
467 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kAudioMid1));
468 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kVideoMid1));
469 // Set the needs-ice-restart flag and verify NeedsIceRestart starts returning
470 // true.
471 transport_controller_->SetNeedsIceRestartFlag();
472 EXPECT_TRUE(transport_controller_->NeedsIceRestart(kAudioMid1));
473 EXPECT_TRUE(transport_controller_->NeedsIceRestart(kVideoMid1));
474 // For a nonexistent transport, false should be returned.
475 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kVideoMid2));
476
477 // Reset the ice_ufrag/ice_pwd for audio.
478 auto audio_transport_info = description->GetTransportInfoByName(kAudioMid1);
479 audio_transport_info->description.ice_ufrag = kIceUfrag2;
480 audio_transport_info->description.ice_pwd = kIcePwd2;
481 EXPECT_TRUE(transport_controller_
482 ->SetLocalDescription(SdpType::kOffer, description.get())
483 .ok());
484 // Because the ICE is only restarted for audio, NeedsIceRestart is expected to
485 // return false for audio and true for video.
486 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kAudioMid1));
487 EXPECT_TRUE(transport_controller_->NeedsIceRestart(kVideoMid1));
488}
489
490TEST_F(JsepTransportControllerTest, MaybeStartGathering) {
491 CreateJsepTransportController(JsepTransportController::Config());
492 auto description = CreateSessionDescriptionWithBundleGroup();
493 EXPECT_TRUE(transport_controller_
494 ->SetLocalDescription(SdpType::kOffer, description.get())
495 .ok());
496 // After setting the local description, we should be able to start gathering
497 // candidates.
498 transport_controller_->MaybeStartGathering();
499 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
500 EXPECT_EQ(1, gathering_state_signal_count_);
501}
502
503TEST_F(JsepTransportControllerTest, AddRemoveRemoteCandidates) {
504 CreateJsepTransportController(JsepTransportController::Config());
505 auto description = CreateSessionDescriptionWithoutBundle();
506 transport_controller_->SetLocalDescription(SdpType::kOffer,
507 description.get());
508 transport_controller_->SetRemoteDescription(SdpType::kAnswer,
509 description.get());
510 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
511 transport_controller_->GetDtlsTransport(kAudioMid1));
512 ASSERT_NE(nullptr, fake_audio_dtls);
513 Candidates candidates;
514 candidates.push_back(
515 CreateCandidate(kAudioMid1, cricket::ICE_CANDIDATE_COMPONENT_RTP));
516 EXPECT_TRUE(
517 transport_controller_->AddRemoteCandidates(kAudioMid1, candidates).ok());
518 EXPECT_EQ(1U,
519 fake_audio_dtls->fake_ice_transport()->remote_candidates().size());
520
521 EXPECT_TRUE(transport_controller_->RemoveRemoteCandidates(candidates).ok());
522 EXPECT_EQ(0U,
523 fake_audio_dtls->fake_ice_transport()->remote_candidates().size());
524}
525
526TEST_F(JsepTransportControllerTest, SetAndGetLocalCertificate) {
527 CreateJsepTransportController(JsepTransportController::Config());
528
529 rtc::scoped_refptr<rtc::RTCCertificate> certificate1 =
530 rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
531 rtc::SSLIdentity::Generate("session1", rtc::KT_DEFAULT)));
532 rtc::scoped_refptr<rtc::RTCCertificate> returned_certificate;
533
Karl Wiberg918f50c2018-07-05 11:40:33 +0200534 auto description = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800535 AddAudioSection(description.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
536 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
537 certificate1);
538
539 // Apply the local certificate.
540 EXPECT_TRUE(transport_controller_->SetLocalCertificate(certificate1));
541 // Apply the local description.
542 EXPECT_TRUE(transport_controller_
543 ->SetLocalDescription(SdpType::kOffer, description.get())
544 .ok());
545 returned_certificate = transport_controller_->GetLocalCertificate(kAudioMid1);
546 EXPECT_TRUE(returned_certificate);
547 EXPECT_EQ(certificate1->identity()->certificate().ToPEMString(),
548 returned_certificate->identity()->certificate().ToPEMString());
549
550 // Should fail if called for a nonexistant transport.
551 EXPECT_EQ(nullptr, transport_controller_->GetLocalCertificate(kVideoMid1));
552
553 // Shouldn't be able to change the identity once set.
554 rtc::scoped_refptr<rtc::RTCCertificate> certificate2 =
555 rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
556 rtc::SSLIdentity::Generate("session2", rtc::KT_DEFAULT)));
557 EXPECT_FALSE(transport_controller_->SetLocalCertificate(certificate2));
558}
559
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800560TEST_F(JsepTransportControllerTest, GetRemoteSSLCertChain) {
Zhi Huange818b6e2018-02-22 15:26:27 -0800561 CreateJsepTransportController(JsepTransportController::Config());
562 auto description = CreateSessionDescriptionWithBundleGroup();
563 EXPECT_TRUE(transport_controller_
564 ->SetLocalDescription(SdpType::kOffer, description.get())
565 .ok());
566 rtc::FakeSSLCertificate fake_certificate("fake_data");
567
568 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
569 transport_controller_->GetDtlsTransport(kAudioMid1));
570 fake_audio_dtls->SetRemoteSSLCertificate(&fake_certificate);
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800571 std::unique_ptr<rtc::SSLCertChain> returned_cert_chain =
572 transport_controller_->GetRemoteSSLCertChain(kAudioMid1);
573 ASSERT_TRUE(returned_cert_chain);
574 ASSERT_EQ(1u, returned_cert_chain->GetSize());
Zhi Huange818b6e2018-02-22 15:26:27 -0800575 EXPECT_EQ(fake_certificate.ToPEMString(),
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800576 returned_cert_chain->Get(0).ToPEMString());
Zhi Huange818b6e2018-02-22 15:26:27 -0800577
578 // Should fail if called for a nonexistant transport.
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800579 EXPECT_FALSE(transport_controller_->GetRemoteSSLCertChain(kAudioMid2));
Zhi Huange818b6e2018-02-22 15:26:27 -0800580}
581
582TEST_F(JsepTransportControllerTest, GetDtlsRole) {
583 CreateJsepTransportController(JsepTransportController::Config());
584 auto offer_certificate =
585 rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
586 rtc::SSLIdentity::Generate("offer", rtc::KT_DEFAULT)));
587 auto answer_certificate =
588 rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
589 rtc::SSLIdentity::Generate("answer", rtc::KT_DEFAULT)));
590 transport_controller_->SetLocalCertificate(offer_certificate);
591
Karl Wiberg918f50c2018-07-05 11:40:33 +0200592 auto offer_desc = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800593 AddAudioSection(offer_desc.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
594 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
595 offer_certificate);
Karl Wiberg918f50c2018-07-05 11:40:33 +0200596 auto answer_desc = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800597 AddAudioSection(answer_desc.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
598 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
599 answer_certificate);
600
601 EXPECT_TRUE(transport_controller_
602 ->SetLocalDescription(SdpType::kOffer, offer_desc.get())
603 .ok());
604
Danil Chapovalov66cadcc2018-06-19 16:47:43 +0200605 absl::optional<rtc::SSLRole> role =
Zhi Huange818b6e2018-02-22 15:26:27 -0800606 transport_controller_->GetDtlsRole(kAudioMid1);
607 // The DTLS role is not decided yet.
608 EXPECT_FALSE(role);
609 EXPECT_TRUE(transport_controller_
610 ->SetRemoteDescription(SdpType::kAnswer, answer_desc.get())
611 .ok());
612 role = transport_controller_->GetDtlsRole(kAudioMid1);
613
614 ASSERT_TRUE(role);
615 EXPECT_EQ(rtc::SSL_CLIENT, *role);
616}
617
618TEST_F(JsepTransportControllerTest, GetStats) {
619 CreateJsepTransportController(JsepTransportController::Config());
620 auto description = CreateSessionDescriptionWithBundleGroup();
621 EXPECT_TRUE(transport_controller_
622 ->SetLocalDescription(SdpType::kOffer, description.get())
623 .ok());
624
625 cricket::TransportStats stats;
626 EXPECT_TRUE(transport_controller_->GetStats(kAudioMid1, &stats));
627 EXPECT_EQ(kAudioMid1, stats.transport_name);
628 EXPECT_EQ(1u, stats.channel_stats.size());
629 // Return false for non-existing transport.
630 EXPECT_FALSE(transport_controller_->GetStats(kAudioMid2, &stats));
631}
632
633TEST_F(JsepTransportControllerTest, SignalConnectionStateFailed) {
634 CreateJsepTransportController(JsepTransportController::Config());
635 auto description = CreateSessionDescriptionWithoutBundle();
636 EXPECT_TRUE(transport_controller_
637 ->SetLocalDescription(SdpType::kOffer, description.get())
638 .ok());
639
640 auto fake_ice = static_cast<cricket::FakeIceTransport*>(
641 transport_controller_->GetDtlsTransport(kAudioMid1)->ice_transport());
642 fake_ice->SetCandidatesGatheringComplete();
643 fake_ice->SetConnectionCount(1);
644 // The connection stats will be failed if there is no active connection.
645 fake_ice->SetConnectionCount(0);
646 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
647 EXPECT_EQ(1, connection_state_signal_count_);
Jonas Olsson635474e2018-10-18 15:58:17 +0200648 EXPECT_EQ(PeerConnectionInterface::kIceConnectionFailed,
649 ice_connection_state_);
650 EXPECT_EQ(1, ice_connection_state_signal_count_);
651 EXPECT_EQ(PeerConnectionInterface::PeerConnectionState::kFailed,
652 combined_connection_state_);
653 EXPECT_EQ(1, combined_connection_state_signal_count_);
Zhi Huange818b6e2018-02-22 15:26:27 -0800654}
655
656TEST_F(JsepTransportControllerTest, SignalConnectionStateConnected) {
657 CreateJsepTransportController(JsepTransportController::Config());
658 auto description = CreateSessionDescriptionWithoutBundle();
659 EXPECT_TRUE(transport_controller_
660 ->SetLocalDescription(SdpType::kOffer, description.get())
661 .ok());
662
663 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
664 transport_controller_->GetDtlsTransport(kAudioMid1));
665 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
666 transport_controller_->GetDtlsTransport(kVideoMid1));
667
668 // First, have one transport connect, and another fail, to ensure that
669 // the first transport connecting didn't trigger a "connected" state signal.
670 // We should only get a signal when all are connected.
671 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
672 fake_audio_dtls->SetWritable(true);
673 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
674 // Decrease the number of the connection to trigger the signal.
675 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
676 fake_video_dtls->fake_ice_transport()->SetConnectionCount(0);
677 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
678
679 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
680 EXPECT_EQ(1, connection_state_signal_count_);
Jonas Olsson635474e2018-10-18 15:58:17 +0200681 EXPECT_EQ(PeerConnectionInterface::kIceConnectionFailed,
682 ice_connection_state_);
683 EXPECT_EQ(1, ice_connection_state_signal_count_);
684 EXPECT_EQ(PeerConnectionInterface::PeerConnectionState::kFailed,
685 combined_connection_state_);
686 EXPECT_EQ(1, combined_connection_state_signal_count_);
Zhi Huange818b6e2018-02-22 15:26:27 -0800687
Jonas Olsson635474e2018-10-18 15:58:17 +0200688 fake_audio_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
689 fake_video_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
Zhi Huange818b6e2018-02-22 15:26:27 -0800690 // Set the connection count to be 2 and the cricket::FakeIceTransport will set
691 // the transport state to be STATE_CONNECTING.
692 fake_video_dtls->fake_ice_transport()->SetConnectionCount(2);
693 fake_video_dtls->SetWritable(true);
694 EXPECT_EQ_WAIT(cricket::kIceConnectionConnected, connection_state_, kTimeout);
695 EXPECT_EQ(2, connection_state_signal_count_);
Jonas Olsson635474e2018-10-18 15:58:17 +0200696 EXPECT_EQ(PeerConnectionInterface::kIceConnectionConnected,
697 ice_connection_state_);
698 EXPECT_EQ(2, ice_connection_state_signal_count_);
699 EXPECT_EQ(PeerConnectionInterface::PeerConnectionState::kConnected,
700 combined_connection_state_);
701 EXPECT_EQ(2, combined_connection_state_signal_count_);
Zhi Huange818b6e2018-02-22 15:26:27 -0800702}
703
704TEST_F(JsepTransportControllerTest, SignalConnectionStateComplete) {
705 CreateJsepTransportController(JsepTransportController::Config());
706 auto description = CreateSessionDescriptionWithoutBundle();
707 EXPECT_TRUE(transport_controller_
708 ->SetLocalDescription(SdpType::kOffer, description.get())
709 .ok());
710
711 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
712 transport_controller_->GetDtlsTransport(kAudioMid1));
713 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
714 transport_controller_->GetDtlsTransport(kVideoMid1));
715
716 // First, have one transport connect, and another fail, to ensure that
717 // the first transport connecting didn't trigger a "connected" state signal.
718 // We should only get a signal when all are connected.
719 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
720 fake_audio_dtls->SetWritable(true);
721 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
722 // Decrease the number of the connection to trigger the signal.
723 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
724 fake_video_dtls->fake_ice_transport()->SetConnectionCount(0);
725 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
726
727 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
728 EXPECT_EQ(1, connection_state_signal_count_);
Jonas Olsson635474e2018-10-18 15:58:17 +0200729 EXPECT_EQ(PeerConnectionInterface::kIceConnectionFailed,
730 ice_connection_state_);
731 EXPECT_EQ(1, ice_connection_state_signal_count_);
732 EXPECT_EQ(PeerConnectionInterface::PeerConnectionState::kFailed,
733 combined_connection_state_);
734 EXPECT_EQ(1, combined_connection_state_signal_count_);
Zhi Huange818b6e2018-02-22 15:26:27 -0800735
Jonas Olsson635474e2018-10-18 15:58:17 +0200736 fake_audio_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
737 fake_video_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
Zhi Huange818b6e2018-02-22 15:26:27 -0800738 // Set the connection count to be 1 and the cricket::FakeIceTransport will set
739 // the transport state to be STATE_COMPLETED.
740 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
741 fake_video_dtls->SetWritable(true);
742 EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
743 EXPECT_EQ(2, connection_state_signal_count_);
Jonas Olsson635474e2018-10-18 15:58:17 +0200744 EXPECT_EQ(PeerConnectionInterface::kIceConnectionCompleted,
745 ice_connection_state_);
746 EXPECT_EQ(2, ice_connection_state_signal_count_);
747 EXPECT_EQ(PeerConnectionInterface::PeerConnectionState::kConnected,
748 combined_connection_state_);
749 EXPECT_EQ(2, combined_connection_state_signal_count_);
Zhi Huange818b6e2018-02-22 15:26:27 -0800750}
751
752TEST_F(JsepTransportControllerTest, SignalIceGatheringStateGathering) {
753 CreateJsepTransportController(JsepTransportController::Config());
754 auto description = CreateSessionDescriptionWithoutBundle();
755 EXPECT_TRUE(transport_controller_
756 ->SetLocalDescription(SdpType::kOffer, description.get())
757 .ok());
758
759 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
760 transport_controller_->GetDtlsTransport(kAudioMid1));
761 fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
762 // Should be in the gathering state as soon as any transport starts gathering.
763 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
764 EXPECT_EQ(1, gathering_state_signal_count_);
765}
766
767TEST_F(JsepTransportControllerTest, SignalIceGatheringStateComplete) {
768 CreateJsepTransportController(JsepTransportController::Config());
769 auto description = CreateSessionDescriptionWithoutBundle();
770 EXPECT_TRUE(transport_controller_
771 ->SetLocalDescription(SdpType::kOffer, description.get())
772 .ok());
773
774 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
775 transport_controller_->GetDtlsTransport(kAudioMid1));
776 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
777 transport_controller_->GetDtlsTransport(kVideoMid1));
778
779 fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
780 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
781 EXPECT_EQ(1, gathering_state_signal_count_);
782
783 // Have one transport finish gathering, to make sure gathering
784 // completion wasn't signalled if only one transport finished gathering.
785 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
786 EXPECT_EQ(1, gathering_state_signal_count_);
787
788 fake_video_dtls->fake_ice_transport()->MaybeStartGathering();
789 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
790 EXPECT_EQ(1, gathering_state_signal_count_);
791
792 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
793 EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
794 EXPECT_EQ(2, gathering_state_signal_count_);
795}
796
797// Test that when the last transport that hasn't finished connecting and/or
798// gathering is destroyed, the aggregate state jumps to "completed". This can
799// happen if, for example, we have an audio and video transport, the audio
800// transport completes, then we start bundling video on the audio transport.
801TEST_F(JsepTransportControllerTest,
802 SignalingWhenLastIncompleteTransportDestroyed) {
803 CreateJsepTransportController(JsepTransportController::Config());
804 auto description = CreateSessionDescriptionWithBundleGroup();
805 EXPECT_TRUE(transport_controller_
806 ->SetLocalDescription(SdpType::kOffer, description.get())
807 .ok());
808
809 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
810 transport_controller_->GetDtlsTransport(kAudioMid1));
811 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
812 transport_controller_->GetDtlsTransport(kVideoMid1));
813 EXPECT_NE(fake_audio_dtls, fake_video_dtls);
814
815 fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
816 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
817 EXPECT_EQ(1, gathering_state_signal_count_);
818
819 // Let the audio transport complete.
820 fake_audio_dtls->SetWritable(true);
821 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
822 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
Jonas Olsson635474e2018-10-18 15:58:17 +0200823 fake_audio_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
Zhi Huange818b6e2018-02-22 15:26:27 -0800824 EXPECT_EQ(1, gathering_state_signal_count_);
825
826 // Set the remote description and enable the bundle.
827 EXPECT_TRUE(transport_controller_
828 ->SetRemoteDescription(SdpType::kAnswer, description.get())
829 .ok());
830 // The BUNDLE should be enabled, the incomplete video transport should be
831 // deleted and the states shoud be updated.
832 fake_video_dtls = static_cast<FakeDtlsTransport*>(
833 transport_controller_->GetDtlsTransport(kVideoMid1));
834 EXPECT_EQ(fake_audio_dtls, fake_video_dtls);
835 EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
Jonas Olsson635474e2018-10-18 15:58:17 +0200836 EXPECT_EQ(PeerConnectionInterface::kIceConnectionCompleted,
837 ice_connection_state_);
838 EXPECT_EQ(PeerConnectionInterface::PeerConnectionState::kConnected,
839 combined_connection_state_);
Zhi Huange818b6e2018-02-22 15:26:27 -0800840 EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
841 EXPECT_EQ(2, gathering_state_signal_count_);
842}
843
844TEST_F(JsepTransportControllerTest, SignalCandidatesGathered) {
845 CreateJsepTransportController(JsepTransportController::Config());
846 auto description = CreateSessionDescriptionWithBundleGroup();
847 EXPECT_TRUE(transport_controller_
848 ->SetLocalDescription(SdpType::kOffer, description.get())
849 .ok());
850 transport_controller_->MaybeStartGathering();
851
852 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
853 transport_controller_->GetDtlsTransport(kAudioMid1));
854 fake_audio_dtls->fake_ice_transport()->SignalCandidateGathered(
855 fake_audio_dtls->fake_ice_transport(), CreateCandidate(kAudioMid1, 1));
856 EXPECT_EQ_WAIT(1, candidates_signal_count_, kTimeout);
857 EXPECT_EQ(1u, candidates_[kAudioMid1].size());
858}
859
860TEST_F(JsepTransportControllerTest, IceSignalingOccursOnSignalingThread) {
861 network_thread_ = rtc::Thread::CreateWithSocketServer();
862 network_thread_->Start();
863 CreateJsepTransportController(JsepTransportController::Config(),
864 signaling_thread_, network_thread_.get(),
865 /*PortAllocator=*/nullptr);
866 CreateLocalDescriptionAndCompleteConnectionOnNetworkThread();
867
868 // connecting --> connected --> completed
869 EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
870 EXPECT_EQ(2, connection_state_signal_count_);
871
872 // new --> gathering --> complete
873 EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
874 EXPECT_EQ(2, gathering_state_signal_count_);
875
876 EXPECT_EQ_WAIT(1u, candidates_[kAudioMid1].size(), kTimeout);
877 EXPECT_EQ_WAIT(1u, candidates_[kVideoMid1].size(), kTimeout);
878 EXPECT_EQ(2, candidates_signal_count_);
879
880 EXPECT_TRUE(!signaled_on_non_signaling_thread_);
881}
882
883// Older versions of Chrome expect the ICE role to be re-determined when an
884// ICE restart occurs, and also don't perform conflict resolution correctly,
885// so for now we can't safely stop doing this.
886// See: https://bugs.chromium.org/p/chromium/issues/detail?id=628676
887// TODO(deadbeef): Remove this when these old versions of Chrome reach a low
888// enough population.
889TEST_F(JsepTransportControllerTest, IceRoleRedeterminedOnIceRestartByDefault) {
890 CreateJsepTransportController(JsepTransportController::Config());
891 // Let the |transport_controller_| be the controlled side initially.
Karl Wiberg918f50c2018-07-05 11:40:33 +0200892 auto remote_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800893 AddAudioSection(remote_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
894 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
895 nullptr);
Karl Wiberg918f50c2018-07-05 11:40:33 +0200896 auto local_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800897 AddAudioSection(local_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
898 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
899 nullptr);
900
901 EXPECT_TRUE(transport_controller_
902 ->SetRemoteDescription(SdpType::kOffer, remote_offer.get())
903 .ok());
904 EXPECT_TRUE(transport_controller_
905 ->SetLocalDescription(SdpType::kAnswer, local_answer.get())
906 .ok());
907
908 auto fake_dtls = static_cast<FakeDtlsTransport*>(
909 transport_controller_->GetDtlsTransport(kAudioMid1));
910 EXPECT_EQ(cricket::ICEROLE_CONTROLLED,
911 fake_dtls->fake_ice_transport()->GetIceRole());
912
913 // New offer will trigger the ICE restart.
Karl Wiberg918f50c2018-07-05 11:40:33 +0200914 auto restart_local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800915 AddAudioSection(restart_local_offer.get(), kAudioMid1, kIceUfrag3, kIcePwd3,
916 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
917 nullptr);
918 EXPECT_TRUE(
919 transport_controller_
920 ->SetLocalDescription(SdpType::kOffer, restart_local_offer.get())
921 .ok());
922 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
923 fake_dtls->fake_ice_transport()->GetIceRole());
924}
925
926// Test that if the TransportController was created with the
927// |redetermine_role_on_ice_restart| parameter set to false, the role is *not*
928// redetermined on an ICE restart.
929TEST_F(JsepTransportControllerTest, IceRoleNotRedetermined) {
930 JsepTransportController::Config config;
931 config.redetermine_role_on_ice_restart = false;
932
933 CreateJsepTransportController(config);
934 // Let the |transport_controller_| be the controlled side initially.
Karl Wiberg918f50c2018-07-05 11:40:33 +0200935 auto remote_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800936 AddAudioSection(remote_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
937 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
938 nullptr);
Karl Wiberg918f50c2018-07-05 11:40:33 +0200939 auto local_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800940 AddAudioSection(local_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
941 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
942 nullptr);
943
944 EXPECT_TRUE(transport_controller_
945 ->SetRemoteDescription(SdpType::kOffer, remote_offer.get())
946 .ok());
947 EXPECT_TRUE(transport_controller_
948 ->SetLocalDescription(SdpType::kAnswer, local_answer.get())
949 .ok());
950
951 auto fake_dtls = static_cast<FakeDtlsTransport*>(
952 transport_controller_->GetDtlsTransport(kAudioMid1));
953 EXPECT_EQ(cricket::ICEROLE_CONTROLLED,
954 fake_dtls->fake_ice_transport()->GetIceRole());
955
956 // New offer will trigger the ICE restart.
Karl Wiberg918f50c2018-07-05 11:40:33 +0200957 auto restart_local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800958 AddAudioSection(restart_local_offer.get(), kAudioMid1, kIceUfrag3, kIcePwd3,
959 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
960 nullptr);
961 EXPECT_TRUE(
962 transport_controller_
963 ->SetLocalDescription(SdpType::kOffer, restart_local_offer.get())
964 .ok());
965 EXPECT_EQ(cricket::ICEROLE_CONTROLLED,
966 fake_dtls->fake_ice_transport()->GetIceRole());
967}
968
969// Tests ICE-Lite mode in remote answer.
970TEST_F(JsepTransportControllerTest, SetIceRoleWhenIceLiteInRemoteAnswer) {
971 CreateJsepTransportController(JsepTransportController::Config());
Karl Wiberg918f50c2018-07-05 11:40:33 +0200972 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800973 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
974 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
975 nullptr);
976 EXPECT_TRUE(transport_controller_
977 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
978 .ok());
979 auto fake_dtls = static_cast<FakeDtlsTransport*>(
980 transport_controller_->GetDtlsTransport(kAudioMid1));
981 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
982 fake_dtls->fake_ice_transport()->GetIceRole());
983 EXPECT_EQ(cricket::ICEMODE_FULL,
984 fake_dtls->fake_ice_transport()->remote_ice_mode());
985
Karl Wiberg918f50c2018-07-05 11:40:33 +0200986 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800987 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
988 cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_PASSIVE,
989 nullptr);
990 EXPECT_TRUE(transport_controller_
991 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
992 .ok());
993 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
994 fake_dtls->fake_ice_transport()->GetIceRole());
995 EXPECT_EQ(cricket::ICEMODE_LITE,
996 fake_dtls->fake_ice_transport()->remote_ice_mode());
997}
998
999// Tests that the ICE role remains "controlling" if a subsequent offer that
1000// does an ICE restart is received from an ICE lite endpoint. Regression test
1001// for: https://crbug.com/710760
1002TEST_F(JsepTransportControllerTest,
1003 IceRoleIsControllingAfterIceRestartFromIceLiteEndpoint) {
1004 CreateJsepTransportController(JsepTransportController::Config());
Karl Wiberg918f50c2018-07-05 11:40:33 +02001005 auto remote_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001006 AddAudioSection(remote_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1007 cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_ACTPASS,
1008 nullptr);
Karl Wiberg918f50c2018-07-05 11:40:33 +02001009 auto local_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001010 AddAudioSection(local_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1011 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1012 nullptr);
1013 // Initial Offer/Answer exchange. If the remote offerer is ICE-Lite, then the
1014 // local side is the controlling.
1015 EXPECT_TRUE(transport_controller_
1016 ->SetRemoteDescription(SdpType::kOffer, remote_offer.get())
1017 .ok());
1018 EXPECT_TRUE(transport_controller_
1019 ->SetLocalDescription(SdpType::kAnswer, local_answer.get())
1020 .ok());
1021 auto fake_dtls = static_cast<FakeDtlsTransport*>(
1022 transport_controller_->GetDtlsTransport(kAudioMid1));
1023 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
1024 fake_dtls->fake_ice_transport()->GetIceRole());
1025
1026 // In the subsequence remote offer triggers an ICE restart.
Karl Wiberg918f50c2018-07-05 11:40:33 +02001027 auto remote_offer2 = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001028 AddAudioSection(remote_offer2.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1029 cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_ACTPASS,
1030 nullptr);
Karl Wiberg918f50c2018-07-05 11:40:33 +02001031 auto local_answer2 = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001032 AddAudioSection(local_answer2.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1033 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1034 nullptr);
1035 EXPECT_TRUE(transport_controller_
1036 ->SetRemoteDescription(SdpType::kOffer, remote_offer2.get())
1037 .ok());
1038 EXPECT_TRUE(transport_controller_
1039 ->SetLocalDescription(SdpType::kAnswer, local_answer2.get())
1040 .ok());
1041 fake_dtls = static_cast<FakeDtlsTransport*>(
1042 transport_controller_->GetDtlsTransport(kAudioMid1));
1043 // The local side is still the controlling role since the remote side is using
1044 // ICE-Lite.
1045 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
1046 fake_dtls->fake_ice_transport()->GetIceRole());
1047}
1048
1049// Tests that the SDP has more than one audio/video m= sections.
1050TEST_F(JsepTransportControllerTest, MultipleMediaSectionsOfSameTypeWithBundle) {
1051 CreateJsepTransportController(JsepTransportController::Config());
1052 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1053 bundle_group.AddContentName(kAudioMid1);
1054 bundle_group.AddContentName(kAudioMid2);
1055 bundle_group.AddContentName(kVideoMid1);
1056 bundle_group.AddContentName(kDataMid1);
1057
Karl Wiberg918f50c2018-07-05 11:40:33 +02001058 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001059 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1060 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1061 nullptr);
1062 AddAudioSection(local_offer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
1063 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1064 nullptr);
1065 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1066 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1067 nullptr);
1068 AddDataSection(local_offer.get(), kDataMid1,
1069 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
1070 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1071 nullptr);
1072
Karl Wiberg918f50c2018-07-05 11:40:33 +02001073 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001074 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1075 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1076 nullptr);
1077 AddAudioSection(remote_answer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
1078 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1079 nullptr);
1080 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1081 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1082 nullptr);
1083 AddDataSection(remote_answer.get(), kDataMid1,
1084 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
1085 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1086 nullptr);
1087
1088 local_offer->AddGroup(bundle_group);
1089 remote_answer->AddGroup(bundle_group);
1090
1091 EXPECT_TRUE(transport_controller_
1092 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1093 .ok());
1094 EXPECT_TRUE(transport_controller_
1095 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1096 .ok());
1097 // Verify that all the sections are bundled on kAudio1.
1098 auto transport1 = transport_controller_->GetRtpTransport(kAudioMid1);
1099 auto transport2 = transport_controller_->GetRtpTransport(kAudioMid2);
1100 auto transport3 = transport_controller_->GetRtpTransport(kVideoMid1);
1101 auto transport4 = transport_controller_->GetRtpTransport(kDataMid1);
1102 EXPECT_EQ(transport1, transport2);
1103 EXPECT_EQ(transport1, transport3);
1104 EXPECT_EQ(transport1, transport4);
1105
1106 // Verify the OnRtpTransport/DtlsTransportChanged signals are fired correctly.
1107 auto it = changed_rtp_transport_by_mid_.find(kAudioMid2);
1108 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1109 EXPECT_EQ(transport1, it->second);
1110 it = changed_rtp_transport_by_mid_.find(kAudioMid2);
1111 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1112 EXPECT_EQ(transport1, it->second);
1113 it = changed_rtp_transport_by_mid_.find(kVideoMid1);
1114 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1115 EXPECT_EQ(transport1, it->second);
1116 // Verify the DtlsTransport for the SCTP data channel is reset correctly.
1117 auto it2 = changed_dtls_transport_by_mid_.find(kDataMid1);
1118 ASSERT_TRUE(it2 != changed_dtls_transport_by_mid_.end());
1119 EXPECT_EQ(transport1->rtp_packet_transport(), it2->second);
1120}
1121
1122// Tests that only a subset of all the m= sections are bundled.
1123TEST_F(JsepTransportControllerTest, BundleSubsetOfMediaSections) {
1124 CreateJsepTransportController(JsepTransportController::Config());
1125 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1126 bundle_group.AddContentName(kAudioMid1);
1127 bundle_group.AddContentName(kVideoMid1);
1128
Karl Wiberg918f50c2018-07-05 11:40:33 +02001129 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001130 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1131 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1132 nullptr);
1133 AddAudioSection(local_offer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
1134 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1135 nullptr);
1136 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1137 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1138 nullptr);
1139
Karl Wiberg918f50c2018-07-05 11:40:33 +02001140 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001141 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1142 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1143 nullptr);
1144 AddAudioSection(remote_answer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
1145 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1146 nullptr);
1147 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1148 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1149 nullptr);
1150
1151 local_offer->AddGroup(bundle_group);
1152 remote_answer->AddGroup(bundle_group);
1153 EXPECT_TRUE(transport_controller_
1154 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1155 .ok());
1156 EXPECT_TRUE(transport_controller_
1157 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1158 .ok());
1159
1160 // Verifiy that only |kAudio1| and |kVideo1| are bundled.
1161 auto transport1 = transport_controller_->GetRtpTransport(kAudioMid1);
1162 auto transport2 = transport_controller_->GetRtpTransport(kAudioMid2);
1163 auto transport3 = transport_controller_->GetRtpTransport(kVideoMid1);
1164 EXPECT_NE(transport1, transport2);
1165 EXPECT_EQ(transport1, transport3);
1166
1167 auto it = changed_rtp_transport_by_mid_.find(kVideoMid1);
1168 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1169 EXPECT_EQ(transport1, it->second);
1170 it = changed_rtp_transport_by_mid_.find(kAudioMid2);
Zhi Huangd2248f82018-04-10 14:41:03 -07001171 EXPECT_TRUE(transport2 == it->second);
Zhi Huange818b6e2018-02-22 15:26:27 -08001172}
1173
1174// Tests that the initial offer/answer only have data section and audio/video
1175// sections are added in the subsequent offer.
1176TEST_F(JsepTransportControllerTest, BundleOnDataSectionInSubsequentOffer) {
1177 CreateJsepTransportController(JsepTransportController::Config());
1178 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1179 bundle_group.AddContentName(kDataMid1);
1180
Karl Wiberg918f50c2018-07-05 11:40:33 +02001181 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001182 AddDataSection(local_offer.get(), kDataMid1,
1183 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
1184 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1185 nullptr);
Karl Wiberg918f50c2018-07-05 11:40:33 +02001186 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001187 AddDataSection(remote_answer.get(), kDataMid1,
1188 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
1189 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1190 nullptr);
1191 local_offer->AddGroup(bundle_group);
1192 remote_answer->AddGroup(bundle_group);
1193
1194 EXPECT_TRUE(transport_controller_
1195 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1196 .ok());
1197 EXPECT_TRUE(transport_controller_
1198 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1199 .ok());
1200 auto data_transport = transport_controller_->GetRtpTransport(kDataMid1);
1201
1202 // Add audio/video sections in subsequent offer.
1203 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1204 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1205 nullptr);
1206 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1207 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1208 nullptr);
1209 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1210 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1211 nullptr);
1212 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1213 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1214 nullptr);
1215
1216 // Reset the bundle group and do another offer/answer exchange.
1217 bundle_group.AddContentName(kAudioMid1);
1218 bundle_group.AddContentName(kVideoMid1);
1219 local_offer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1220 remote_answer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1221 local_offer->AddGroup(bundle_group);
1222 remote_answer->AddGroup(bundle_group);
1223
1224 EXPECT_TRUE(transport_controller_
1225 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1226 .ok());
1227 EXPECT_TRUE(transport_controller_
1228 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1229 .ok());
1230
1231 auto audio_transport = transport_controller_->GetRtpTransport(kAudioMid1);
1232 auto video_transport = transport_controller_->GetRtpTransport(kVideoMid1);
1233 EXPECT_EQ(data_transport, audio_transport);
1234 EXPECT_EQ(data_transport, video_transport);
1235}
1236
1237TEST_F(JsepTransportControllerTest, VideoDataRejectedInAnswer) {
1238 CreateJsepTransportController(JsepTransportController::Config());
1239 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1240 bundle_group.AddContentName(kAudioMid1);
1241 bundle_group.AddContentName(kVideoMid1);
1242 bundle_group.AddContentName(kDataMid1);
1243
Karl Wiberg918f50c2018-07-05 11:40:33 +02001244 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001245 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1246 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1247 nullptr);
1248 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1249 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1250 nullptr);
1251 AddDataSection(local_offer.get(), kDataMid1,
1252 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1253 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1254 nullptr);
1255
Karl Wiberg918f50c2018-07-05 11:40:33 +02001256 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001257 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1258 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1259 nullptr);
1260 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1261 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1262 nullptr);
1263 AddDataSection(remote_answer.get(), kDataMid1,
1264 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1265 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1266 nullptr);
1267 // Reject video and data section.
1268 remote_answer->contents()[1].rejected = true;
1269 remote_answer->contents()[2].rejected = true;
1270
1271 local_offer->AddGroup(bundle_group);
1272 remote_answer->AddGroup(bundle_group);
1273
1274 EXPECT_TRUE(transport_controller_
1275 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1276 .ok());
1277 EXPECT_TRUE(transport_controller_
1278 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1279 .ok());
1280
1281 // Verify the RtpTransport/DtlsTransport is destroyed correctly.
1282 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kVideoMid1));
1283 EXPECT_EQ(nullptr, transport_controller_->GetDtlsTransport(kDataMid1));
1284 // Verify the signals are fired correctly.
1285 auto it = changed_rtp_transport_by_mid_.find(kVideoMid1);
1286 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1287 EXPECT_EQ(nullptr, it->second);
1288 auto it2 = changed_dtls_transport_by_mid_.find(kDataMid1);
1289 ASSERT_TRUE(it2 != changed_dtls_transport_by_mid_.end());
1290 EXPECT_EQ(nullptr, it2->second);
1291}
1292
1293// Tests that changing the bundled MID in subsequent offer/answer exchange is
1294// not supported.
1295// TODO(bugs.webrtc.org/6704): Change this test to expect success once issue is
1296// fixed
1297TEST_F(JsepTransportControllerTest, ChangeBundledMidNotSupported) {
1298 CreateJsepTransportController(JsepTransportController::Config());
1299 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1300 bundle_group.AddContentName(kAudioMid1);
1301 bundle_group.AddContentName(kVideoMid1);
1302
Karl Wiberg918f50c2018-07-05 11:40:33 +02001303 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001304 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1305 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1306 nullptr);
1307 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1308 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1309 nullptr);
1310
Karl Wiberg918f50c2018-07-05 11:40:33 +02001311 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001312 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1313 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1314 nullptr);
1315 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1316 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1317 nullptr);
1318
1319 local_offer->AddGroup(bundle_group);
1320 remote_answer->AddGroup(bundle_group);
1321 EXPECT_TRUE(transport_controller_
1322 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1323 .ok());
1324 EXPECT_TRUE(transport_controller_
1325 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1326 .ok());
1327 EXPECT_EQ(transport_controller_->GetRtpTransport(kAudioMid1),
1328 transport_controller_->GetRtpTransport(kVideoMid1));
1329
1330 // Reorder the bundle group.
1331 EXPECT_TRUE(bundle_group.RemoveContentName(kAudioMid1));
1332 bundle_group.AddContentName(kAudioMid1);
1333 // The answerer uses the new bundle group and now the bundle mid is changed to
1334 // |kVideo1|.
1335 remote_answer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1336 remote_answer->AddGroup(bundle_group);
1337 EXPECT_TRUE(transport_controller_
1338 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1339 .ok());
1340 EXPECT_FALSE(transport_controller_
1341 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1342 .ok());
1343}
Zhi Huange830e682018-03-30 10:48:35 -07001344// Test that rejecting only the first m= section of a BUNDLE group is treated as
1345// an error, but rejecting all of them works as expected.
1346TEST_F(JsepTransportControllerTest, RejectFirstContentInBundleGroup) {
1347 CreateJsepTransportController(JsepTransportController::Config());
1348 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1349 bundle_group.AddContentName(kAudioMid1);
1350 bundle_group.AddContentName(kVideoMid1);
1351 bundle_group.AddContentName(kDataMid1);
1352
Karl Wiberg918f50c2018-07-05 11:40:33 +02001353 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange830e682018-03-30 10:48:35 -07001354 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1355 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1356 nullptr);
1357 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1358 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1359 nullptr);
1360 AddDataSection(local_offer.get(), kDataMid1,
1361 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1362 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1363 nullptr);
1364
Karl Wiberg918f50c2018-07-05 11:40:33 +02001365 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange830e682018-03-30 10:48:35 -07001366 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1367 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1368 nullptr);
1369 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1370 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1371 nullptr);
1372 AddDataSection(remote_answer.get(), kDataMid1,
1373 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1374 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1375 nullptr);
1376 // Reject audio content in answer.
1377 remote_answer->contents()[0].rejected = true;
1378
1379 local_offer->AddGroup(bundle_group);
1380 remote_answer->AddGroup(bundle_group);
1381
1382 EXPECT_TRUE(transport_controller_
1383 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1384 .ok());
1385 EXPECT_FALSE(transport_controller_
1386 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1387 .ok());
1388
1389 // Reject all the contents.
1390 remote_answer->contents()[1].rejected = true;
1391 remote_answer->contents()[2].rejected = true;
1392 EXPECT_TRUE(transport_controller_
1393 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1394 .ok());
1395 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kAudioMid1));
1396 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kVideoMid1));
1397 EXPECT_EQ(nullptr, transport_controller_->GetDtlsTransport(kDataMid1));
1398}
1399
1400// Tests that applying non-RTCP-mux offer would fail when kRtcpMuxPolicyRequire
1401// is used.
1402TEST_F(JsepTransportControllerTest, ApplyNonRtcpMuxOfferWhenMuxingRequired) {
1403 JsepTransportController::Config config;
1404 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
1405 CreateJsepTransportController(config);
Karl Wiberg918f50c2018-07-05 11:40:33 +02001406 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange830e682018-03-30 10:48:35 -07001407 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1408 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1409 nullptr);
1410
1411 local_offer->contents()[0].media_description()->set_rtcp_mux(false);
1412 // Applying a non-RTCP-mux offer is expected to fail.
1413 EXPECT_FALSE(transport_controller_
1414 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1415 .ok());
1416}
1417
1418// Tests that applying non-RTCP-mux answer would fail when kRtcpMuxPolicyRequire
1419// is used.
1420TEST_F(JsepTransportControllerTest, ApplyNonRtcpMuxAnswerWhenMuxingRequired) {
1421 JsepTransportController::Config config;
1422 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
1423 CreateJsepTransportController(config);
Karl Wiberg918f50c2018-07-05 11:40:33 +02001424 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange830e682018-03-30 10:48:35 -07001425 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1426 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1427 nullptr);
1428 EXPECT_TRUE(transport_controller_
1429 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1430 .ok());
1431
Karl Wiberg918f50c2018-07-05 11:40:33 +02001432 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange830e682018-03-30 10:48:35 -07001433 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1434 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1435 nullptr);
1436 // Applying a non-RTCP-mux answer is expected to fail.
1437 remote_answer->contents()[0].media_description()->set_rtcp_mux(false);
1438 EXPECT_FALSE(transport_controller_
1439 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1440 .ok());
1441}
Zhi Huange818b6e2018-02-22 15:26:27 -08001442
Zhi Huangd2248f82018-04-10 14:41:03 -07001443// This tests that the BUNDLE group in answer should be a subset of the offered
1444// group.
1445TEST_F(JsepTransportControllerTest,
1446 AddContentToBundleGroupInAnswerNotSupported) {
1447 CreateJsepTransportController(JsepTransportController::Config());
1448 auto local_offer = CreateSessionDescriptionWithoutBundle();
1449 auto remote_answer = CreateSessionDescriptionWithoutBundle();
1450
1451 cricket::ContentGroup offer_bundle_group(cricket::GROUP_TYPE_BUNDLE);
1452 offer_bundle_group.AddContentName(kAudioMid1);
1453 local_offer->AddGroup(offer_bundle_group);
1454
1455 cricket::ContentGroup answer_bundle_group(cricket::GROUP_TYPE_BUNDLE);
1456 answer_bundle_group.AddContentName(kAudioMid1);
1457 answer_bundle_group.AddContentName(kVideoMid1);
1458 remote_answer->AddGroup(answer_bundle_group);
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
1467// This tests that the BUNDLE group with non-existing MID should be rejectd.
1468TEST_F(JsepTransportControllerTest, RejectBundleGroupWithNonExistingMid) {
1469 CreateJsepTransportController(JsepTransportController::Config());
1470 auto local_offer = CreateSessionDescriptionWithoutBundle();
1471 auto remote_answer = CreateSessionDescriptionWithoutBundle();
1472
1473 cricket::ContentGroup invalid_bundle_group(cricket::GROUP_TYPE_BUNDLE);
1474 // The BUNDLE group is invalid because there is no data section in the
1475 // description.
1476 invalid_bundle_group.AddContentName(kDataMid1);
1477 local_offer->AddGroup(invalid_bundle_group);
1478 remote_answer->AddGroup(invalid_bundle_group);
1479
1480 EXPECT_FALSE(transport_controller_
1481 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1482 .ok());
1483 EXPECT_FALSE(transport_controller_
1484 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1485 .ok());
1486}
1487
1488// This tests that an answer shouldn't be able to remove an m= section from an
1489// established group without rejecting it.
1490TEST_F(JsepTransportControllerTest, RemoveContentFromBundleGroup) {
1491 CreateJsepTransportController(JsepTransportController::Config());
1492
1493 auto local_offer = CreateSessionDescriptionWithBundleGroup();
1494 auto remote_answer = CreateSessionDescriptionWithBundleGroup();
1495 EXPECT_TRUE(transport_controller_
1496 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1497 .ok());
1498 EXPECT_TRUE(transport_controller_
1499 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1500 .ok());
1501
1502 // Do an re-offer/answer.
1503 EXPECT_TRUE(transport_controller_
1504 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1505 .ok());
1506 auto new_answer = CreateSessionDescriptionWithoutBundle();
1507 cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
1508 // The answer removes video from the BUNDLE group without rejecting it is
1509 // invalid.
1510 new_bundle_group.AddContentName(kAudioMid1);
1511 new_answer->AddGroup(new_bundle_group);
1512
1513 // Applying invalid answer is expected to fail.
1514 EXPECT_FALSE(transport_controller_
1515 ->SetRemoteDescription(SdpType::kAnswer, new_answer.get())
1516 .ok());
1517
1518 // Rejected the video content.
1519 auto video_content = new_answer->GetContentByName(kVideoMid1);
1520 ASSERT_TRUE(video_content);
1521 video_content->rejected = true;
1522 EXPECT_TRUE(transport_controller_
1523 ->SetRemoteDescription(SdpType::kAnswer, new_answer.get())
1524 .ok());
1525}
1526
Zhi Huange818b6e2018-02-22 15:26:27 -08001527} // namespace webrtc