blob: 6f8693bea7a9b57150258d0486c0a7f24eefb3f7 [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
Karl Wiberg918f50c2018-07-05 11:40:33 +020014#include "absl/memory/memory.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,
55 const rtc::CryptoOptions& crypto_options) override {
56 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);
87 transport_controller_->SignalIceGatheringState.connect(
88 this, &JsepTransportControllerTest::OnGatheringState);
89 transport_controller_->SignalIceCandidatesGathered.connect(
90 this, &JsepTransportControllerTest::OnCandidatesGathered);
Zhi Huange818b6e2018-02-22 15:26:27 -080091 }
92
93 std::unique_ptr<cricket::SessionDescription>
94 CreateSessionDescriptionWithoutBundle() {
Karl Wiberg918f50c2018-07-05 11:40:33 +020095 auto description = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -080096 AddAudioSection(description.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
97 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
98 nullptr);
99 AddVideoSection(description.get(), kVideoMid1, kIceUfrag1, kIcePwd1,
100 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
101 nullptr);
102 return description;
103 }
104
105 std::unique_ptr<cricket::SessionDescription>
106 CreateSessionDescriptionWithBundleGroup() {
107 auto description = CreateSessionDescriptionWithoutBundle();
108 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
109 bundle_group.AddContentName(kAudioMid1);
110 bundle_group.AddContentName(kVideoMid1);
111 description->AddGroup(bundle_group);
112
113 return description;
114 }
115
116 void AddAudioSection(cricket::SessionDescription* description,
117 const std::string& mid,
118 const std::string& ufrag,
119 const std::string& pwd,
120 cricket::IceMode ice_mode,
121 cricket::ConnectionRole conn_role,
122 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
123 std::unique_ptr<cricket::AudioContentDescription> audio(
124 new cricket::AudioContentDescription());
Zhi Huange830e682018-03-30 10:48:35 -0700125 // Set RTCP-mux to be true because the default policy is "mux required".
126 audio->set_rtcp_mux(true);
Zhi Huange818b6e2018-02-22 15:26:27 -0800127 description->AddContent(mid, cricket::MediaProtocolType::kRtp,
128 /*rejected=*/false, audio.release());
129 AddTransportInfo(description, mid, ufrag, pwd, ice_mode, conn_role, cert);
130 }
131
132 void AddVideoSection(cricket::SessionDescription* description,
133 const std::string& mid,
134 const std::string& ufrag,
135 const std::string& pwd,
136 cricket::IceMode ice_mode,
137 cricket::ConnectionRole conn_role,
138 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
139 std::unique_ptr<cricket::VideoContentDescription> video(
140 new cricket::VideoContentDescription());
Zhi Huange830e682018-03-30 10:48:35 -0700141 // Set RTCP-mux to be true because the default policy is "mux required".
142 video->set_rtcp_mux(true);
Zhi Huange818b6e2018-02-22 15:26:27 -0800143 description->AddContent(mid, cricket::MediaProtocolType::kRtp,
144 /*rejected=*/false, video.release());
145 AddTransportInfo(description, mid, ufrag, pwd, ice_mode, conn_role, cert);
146 }
147
148 void AddDataSection(cricket::SessionDescription* description,
149 const std::string& mid,
150 cricket::MediaProtocolType protocol_type,
151 const std::string& ufrag,
152 const std::string& pwd,
153 cricket::IceMode ice_mode,
154 cricket::ConnectionRole conn_role,
155 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
156 std::unique_ptr<cricket::DataContentDescription> data(
157 new cricket::DataContentDescription());
Zhi Huange830e682018-03-30 10:48:35 -0700158 data->set_rtcp_mux(true);
Zhi Huange818b6e2018-02-22 15:26:27 -0800159 description->AddContent(mid, protocol_type,
160 /*rejected=*/false, data.release());
161 AddTransportInfo(description, mid, ufrag, pwd, ice_mode, conn_role, cert);
162 }
163
164 void AddTransportInfo(cricket::SessionDescription* description,
165 const std::string& mid,
166 const std::string& ufrag,
167 const std::string& pwd,
168 cricket::IceMode ice_mode,
169 cricket::ConnectionRole conn_role,
170 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
171 std::unique_ptr<rtc::SSLFingerprint> fingerprint;
172 if (cert) {
173 fingerprint.reset(rtc::SSLFingerprint::CreateFromCertificate(cert));
174 }
175
176 cricket::TransportDescription transport_desc(std::vector<std::string>(),
177 ufrag, pwd, ice_mode,
178 conn_role, fingerprint.get());
179 description->AddTransportInfo(cricket::TransportInfo(mid, transport_desc));
180 }
181
182 cricket::IceConfig CreateIceConfig(
183 int receiving_timeout,
184 cricket::ContinualGatheringPolicy continual_gathering_policy) {
185 cricket::IceConfig config;
186 config.receiving_timeout = receiving_timeout;
187 config.continual_gathering_policy = continual_gathering_policy;
188 return config;
189 }
190
191 Candidate CreateCandidate(const std::string& transport_name, int component) {
192 Candidate c;
193 c.set_transport_name(transport_name);
194 c.set_address(rtc::SocketAddress("192.168.1.1", 8000));
195 c.set_component(component);
196 c.set_protocol(cricket::UDP_PROTOCOL_NAME);
197 c.set_priority(1);
198 return c;
199 }
200
201 void CreateLocalDescriptionAndCompleteConnectionOnNetworkThread() {
202 if (!network_thread_->IsCurrent()) {
203 network_thread_->Invoke<void>(RTC_FROM_HERE, [&] {
204 CreateLocalDescriptionAndCompleteConnectionOnNetworkThread();
205 });
206 return;
207 }
208
209 auto description = CreateSessionDescriptionWithBundleGroup();
210 EXPECT_TRUE(transport_controller_
211 ->SetLocalDescription(SdpType::kOffer, description.get())
212 .ok());
213
214 transport_controller_->MaybeStartGathering();
215 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
216 transport_controller_->GetDtlsTransport(kAudioMid1));
217 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
218 transport_controller_->GetDtlsTransport(kVideoMid1));
219 fake_audio_dtls->fake_ice_transport()->SignalCandidateGathered(
220 fake_audio_dtls->fake_ice_transport(),
221 CreateCandidate(kAudioMid1, /*component=*/1));
222 fake_video_dtls->fake_ice_transport()->SignalCandidateGathered(
223 fake_video_dtls->fake_ice_transport(),
224 CreateCandidate(kVideoMid1, /*component=*/1));
225 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
226 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
227 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(2);
228 fake_video_dtls->fake_ice_transport()->SetConnectionCount(2);
229 fake_audio_dtls->SetReceiving(true);
230 fake_video_dtls->SetReceiving(true);
231 fake_audio_dtls->SetWritable(true);
232 fake_video_dtls->SetWritable(true);
233 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
234 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
235 }
236
237 protected:
238 void OnConnectionState(cricket::IceConnectionState state) {
239 if (!signaling_thread_->IsCurrent()) {
240 signaled_on_non_signaling_thread_ = true;
241 }
242 connection_state_ = state;
243 ++connection_state_signal_count_;
244 }
245
246 void OnGatheringState(cricket::IceGatheringState state) {
247 if (!signaling_thread_->IsCurrent()) {
248 signaled_on_non_signaling_thread_ = true;
249 }
250 gathering_state_ = state;
251 ++gathering_state_signal_count_;
252 }
253
254 void OnCandidatesGathered(const std::string& transport_name,
255 const Candidates& candidates) {
256 if (!signaling_thread_->IsCurrent()) {
257 signaled_on_non_signaling_thread_ = true;
258 }
259 candidates_[transport_name].insert(candidates_[transport_name].end(),
260 candidates.begin(), candidates.end());
261 ++candidates_signal_count_;
262 }
263
Zhi Huang365381f2018-04-13 16:44:34 -0700264 // JsepTransportController::Observer overrides.
Taylor Brandstettercbaa2542018-04-16 16:42:14 -0700265 bool OnTransportChanged(
Zhi Huang365381f2018-04-13 16:44:34 -0700266 const std::string& mid,
Taylor Brandstettercbaa2542018-04-16 16:42:14 -0700267 RtpTransportInternal* rtp_transport,
Zhi Huang365381f2018-04-13 16:44:34 -0700268 cricket::DtlsTransportInternal* dtls_transport) override {
Taylor Brandstettercbaa2542018-04-16 16:42:14 -0700269 changed_rtp_transport_by_mid_[mid] = rtp_transport;
Zhi Huange818b6e2018-02-22 15:26:27 -0800270 changed_dtls_transport_by_mid_[mid] = dtls_transport;
Taylor Brandstettercbaa2542018-04-16 16:42:14 -0700271 return true;
Zhi Huange818b6e2018-02-22 15:26:27 -0800272 }
273
274 // Information received from signals from transport controller.
275 cricket::IceConnectionState connection_state_ =
276 cricket::kIceConnectionConnecting;
277 bool receiving_ = false;
278 cricket::IceGatheringState gathering_state_ = cricket::kIceGatheringNew;
279 // transport_name => candidates
280 std::map<std::string, Candidates> candidates_;
281 // Counts of each signal emitted.
282 int connection_state_signal_count_ = 0;
283 int receiving_signal_count_ = 0;
284 int gathering_state_signal_count_ = 0;
285 int candidates_signal_count_ = 0;
286
287 // |network_thread_| should be destroyed after |transport_controller_|
288 std::unique_ptr<rtc::Thread> network_thread_;
289 std::unique_ptr<JsepTransportController> transport_controller_;
290 std::unique_ptr<FakeTransportFactory> fake_transport_factory_;
291 rtc::Thread* const signaling_thread_ = nullptr;
292 bool signaled_on_non_signaling_thread_ = false;
293 // Used to verify the SignalRtpTransportChanged/SignalDtlsTransportChanged are
294 // signaled correctly.
295 std::map<std::string, RtpTransportInternal*> changed_rtp_transport_by_mid_;
296 std::map<std::string, cricket::DtlsTransportInternal*>
297 changed_dtls_transport_by_mid_;
298};
299
300TEST_F(JsepTransportControllerTest, GetRtpTransport) {
301 CreateJsepTransportController(JsepTransportController::Config());
302 auto description = CreateSessionDescriptionWithoutBundle();
303 EXPECT_TRUE(transport_controller_
304 ->SetLocalDescription(SdpType::kOffer, description.get())
305 .ok());
306 auto audio_rtp_transport = transport_controller_->GetRtpTransport(kAudioMid1);
307 auto video_rtp_transport = transport_controller_->GetRtpTransport(kVideoMid1);
308 EXPECT_NE(nullptr, audio_rtp_transport);
309 EXPECT_NE(nullptr, video_rtp_transport);
310 EXPECT_NE(audio_rtp_transport, video_rtp_transport);
311 // Return nullptr for non-existing ones.
312 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kAudioMid2));
313}
314
315TEST_F(JsepTransportControllerTest, GetDtlsTransport) {
316 JsepTransportController::Config config;
317 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
318 CreateJsepTransportController(config);
319 auto description = CreateSessionDescriptionWithoutBundle();
320 EXPECT_TRUE(transport_controller_
321 ->SetLocalDescription(SdpType::kOffer, description.get())
322 .ok());
323 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kAudioMid1));
324 EXPECT_NE(nullptr, transport_controller_->GetRtcpDtlsTransport(kAudioMid1));
325 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kVideoMid1));
326 EXPECT_NE(nullptr, transport_controller_->GetRtcpDtlsTransport(kVideoMid1));
327 // Return nullptr for non-existing ones.
328 EXPECT_EQ(nullptr, transport_controller_->GetDtlsTransport(kVideoMid2));
329 EXPECT_EQ(nullptr, transport_controller_->GetRtcpDtlsTransport(kVideoMid2));
330}
331
Zhi Huange830e682018-03-30 10:48:35 -0700332TEST_F(JsepTransportControllerTest, GetDtlsTransportWithRtcpMux) {
333 JsepTransportController::Config config;
334 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
335 CreateJsepTransportController(config);
336 auto description = CreateSessionDescriptionWithoutBundle();
337 EXPECT_TRUE(transport_controller_
338 ->SetLocalDescription(SdpType::kOffer, description.get())
339 .ok());
340 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kAudioMid1));
341 EXPECT_EQ(nullptr, transport_controller_->GetRtcpDtlsTransport(kAudioMid1));
342 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kVideoMid1));
343 EXPECT_EQ(nullptr, transport_controller_->GetRtcpDtlsTransport(kVideoMid1));
344}
345
Zhi Huange818b6e2018-02-22 15:26:27 -0800346TEST_F(JsepTransportControllerTest, SetIceConfig) {
347 CreateJsepTransportController(JsepTransportController::Config());
348 auto description = CreateSessionDescriptionWithoutBundle();
349 EXPECT_TRUE(transport_controller_
350 ->SetLocalDescription(SdpType::kOffer, description.get())
351 .ok());
352
353 transport_controller_->SetIceConfig(
354 CreateIceConfig(kTimeout, cricket::GATHER_CONTINUALLY));
355 FakeDtlsTransport* fake_audio_dtls = static_cast<FakeDtlsTransport*>(
356 transport_controller_->GetDtlsTransport(kAudioMid1));
357 ASSERT_NE(nullptr, fake_audio_dtls);
358 EXPECT_EQ(kTimeout,
359 fake_audio_dtls->fake_ice_transport()->receiving_timeout());
360 EXPECT_TRUE(fake_audio_dtls->fake_ice_transport()->gather_continually());
361
362 // Test that value stored in controller is applied to new transports.
363 AddAudioSection(description.get(), kAudioMid2, kIceUfrag1, kIcePwd1,
364 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
365 nullptr);
366
367 EXPECT_TRUE(transport_controller_
368 ->SetLocalDescription(SdpType::kOffer, description.get())
369 .ok());
370 fake_audio_dtls = static_cast<FakeDtlsTransport*>(
371 transport_controller_->GetDtlsTransport(kAudioMid2));
372 ASSERT_NE(nullptr, fake_audio_dtls);
373 EXPECT_EQ(kTimeout,
374 fake_audio_dtls->fake_ice_transport()->receiving_timeout());
375 EXPECT_TRUE(fake_audio_dtls->fake_ice_transport()->gather_continually());
376}
377
378// Tests the getter and setter of the ICE restart flag.
379TEST_F(JsepTransportControllerTest, NeedIceRestart) {
380 CreateJsepTransportController(JsepTransportController::Config());
381 auto description = CreateSessionDescriptionWithoutBundle();
382 EXPECT_TRUE(transport_controller_
383 ->SetLocalDescription(SdpType::kOffer, description.get())
384 .ok());
385 EXPECT_TRUE(transport_controller_
386 ->SetRemoteDescription(SdpType::kAnswer, description.get())
387 .ok());
388
389 // Initially NeedsIceRestart should return false.
390 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kAudioMid1));
391 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kVideoMid1));
392 // Set the needs-ice-restart flag and verify NeedsIceRestart starts returning
393 // true.
394 transport_controller_->SetNeedsIceRestartFlag();
395 EXPECT_TRUE(transport_controller_->NeedsIceRestart(kAudioMid1));
396 EXPECT_TRUE(transport_controller_->NeedsIceRestart(kVideoMid1));
397 // For a nonexistent transport, false should be returned.
398 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kVideoMid2));
399
400 // Reset the ice_ufrag/ice_pwd for audio.
401 auto audio_transport_info = description->GetTransportInfoByName(kAudioMid1);
402 audio_transport_info->description.ice_ufrag = kIceUfrag2;
403 audio_transport_info->description.ice_pwd = kIcePwd2;
404 EXPECT_TRUE(transport_controller_
405 ->SetLocalDescription(SdpType::kOffer, description.get())
406 .ok());
407 // Because the ICE is only restarted for audio, NeedsIceRestart is expected to
408 // return false for audio and true for video.
409 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kAudioMid1));
410 EXPECT_TRUE(transport_controller_->NeedsIceRestart(kVideoMid1));
411}
412
413TEST_F(JsepTransportControllerTest, MaybeStartGathering) {
414 CreateJsepTransportController(JsepTransportController::Config());
415 auto description = CreateSessionDescriptionWithBundleGroup();
416 EXPECT_TRUE(transport_controller_
417 ->SetLocalDescription(SdpType::kOffer, description.get())
418 .ok());
419 // After setting the local description, we should be able to start gathering
420 // candidates.
421 transport_controller_->MaybeStartGathering();
422 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
423 EXPECT_EQ(1, gathering_state_signal_count_);
424}
425
426TEST_F(JsepTransportControllerTest, AddRemoveRemoteCandidates) {
427 CreateJsepTransportController(JsepTransportController::Config());
428 auto description = CreateSessionDescriptionWithoutBundle();
429 transport_controller_->SetLocalDescription(SdpType::kOffer,
430 description.get());
431 transport_controller_->SetRemoteDescription(SdpType::kAnswer,
432 description.get());
433 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
434 transport_controller_->GetDtlsTransport(kAudioMid1));
435 ASSERT_NE(nullptr, fake_audio_dtls);
436 Candidates candidates;
437 candidates.push_back(
438 CreateCandidate(kAudioMid1, cricket::ICE_CANDIDATE_COMPONENT_RTP));
439 EXPECT_TRUE(
440 transport_controller_->AddRemoteCandidates(kAudioMid1, candidates).ok());
441 EXPECT_EQ(1U,
442 fake_audio_dtls->fake_ice_transport()->remote_candidates().size());
443
444 EXPECT_TRUE(transport_controller_->RemoveRemoteCandidates(candidates).ok());
445 EXPECT_EQ(0U,
446 fake_audio_dtls->fake_ice_transport()->remote_candidates().size());
447}
448
449TEST_F(JsepTransportControllerTest, SetAndGetLocalCertificate) {
450 CreateJsepTransportController(JsepTransportController::Config());
451
452 rtc::scoped_refptr<rtc::RTCCertificate> certificate1 =
453 rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
454 rtc::SSLIdentity::Generate("session1", rtc::KT_DEFAULT)));
455 rtc::scoped_refptr<rtc::RTCCertificate> returned_certificate;
456
Karl Wiberg918f50c2018-07-05 11:40:33 +0200457 auto description = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800458 AddAudioSection(description.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
459 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
460 certificate1);
461
462 // Apply the local certificate.
463 EXPECT_TRUE(transport_controller_->SetLocalCertificate(certificate1));
464 // Apply the local description.
465 EXPECT_TRUE(transport_controller_
466 ->SetLocalDescription(SdpType::kOffer, description.get())
467 .ok());
468 returned_certificate = transport_controller_->GetLocalCertificate(kAudioMid1);
469 EXPECT_TRUE(returned_certificate);
470 EXPECT_EQ(certificate1->identity()->certificate().ToPEMString(),
471 returned_certificate->identity()->certificate().ToPEMString());
472
473 // Should fail if called for a nonexistant transport.
474 EXPECT_EQ(nullptr, transport_controller_->GetLocalCertificate(kVideoMid1));
475
476 // Shouldn't be able to change the identity once set.
477 rtc::scoped_refptr<rtc::RTCCertificate> certificate2 =
478 rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
479 rtc::SSLIdentity::Generate("session2", rtc::KT_DEFAULT)));
480 EXPECT_FALSE(transport_controller_->SetLocalCertificate(certificate2));
481}
482
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800483TEST_F(JsepTransportControllerTest, GetRemoteSSLCertChain) {
Zhi Huange818b6e2018-02-22 15:26:27 -0800484 CreateJsepTransportController(JsepTransportController::Config());
485 auto description = CreateSessionDescriptionWithBundleGroup();
486 EXPECT_TRUE(transport_controller_
487 ->SetLocalDescription(SdpType::kOffer, description.get())
488 .ok());
489 rtc::FakeSSLCertificate fake_certificate("fake_data");
490
491 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
492 transport_controller_->GetDtlsTransport(kAudioMid1));
493 fake_audio_dtls->SetRemoteSSLCertificate(&fake_certificate);
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800494 std::unique_ptr<rtc::SSLCertChain> returned_cert_chain =
495 transport_controller_->GetRemoteSSLCertChain(kAudioMid1);
496 ASSERT_TRUE(returned_cert_chain);
497 ASSERT_EQ(1u, returned_cert_chain->GetSize());
Zhi Huange818b6e2018-02-22 15:26:27 -0800498 EXPECT_EQ(fake_certificate.ToPEMString(),
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800499 returned_cert_chain->Get(0).ToPEMString());
Zhi Huange818b6e2018-02-22 15:26:27 -0800500
501 // Should fail if called for a nonexistant transport.
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800502 EXPECT_FALSE(transport_controller_->GetRemoteSSLCertChain(kAudioMid2));
Zhi Huange818b6e2018-02-22 15:26:27 -0800503}
504
505TEST_F(JsepTransportControllerTest, GetDtlsRole) {
506 CreateJsepTransportController(JsepTransportController::Config());
507 auto offer_certificate =
508 rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
509 rtc::SSLIdentity::Generate("offer", rtc::KT_DEFAULT)));
510 auto answer_certificate =
511 rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
512 rtc::SSLIdentity::Generate("answer", rtc::KT_DEFAULT)));
513 transport_controller_->SetLocalCertificate(offer_certificate);
514
Karl Wiberg918f50c2018-07-05 11:40:33 +0200515 auto offer_desc = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800516 AddAudioSection(offer_desc.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
517 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
518 offer_certificate);
Karl Wiberg918f50c2018-07-05 11:40:33 +0200519 auto answer_desc = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800520 AddAudioSection(answer_desc.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
521 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
522 answer_certificate);
523
524 EXPECT_TRUE(transport_controller_
525 ->SetLocalDescription(SdpType::kOffer, offer_desc.get())
526 .ok());
527
Danil Chapovalov66cadcc2018-06-19 16:47:43 +0200528 absl::optional<rtc::SSLRole> role =
Zhi Huange818b6e2018-02-22 15:26:27 -0800529 transport_controller_->GetDtlsRole(kAudioMid1);
530 // The DTLS role is not decided yet.
531 EXPECT_FALSE(role);
532 EXPECT_TRUE(transport_controller_
533 ->SetRemoteDescription(SdpType::kAnswer, answer_desc.get())
534 .ok());
535 role = transport_controller_->GetDtlsRole(kAudioMid1);
536
537 ASSERT_TRUE(role);
538 EXPECT_EQ(rtc::SSL_CLIENT, *role);
539}
540
541TEST_F(JsepTransportControllerTest, GetStats) {
542 CreateJsepTransportController(JsepTransportController::Config());
543 auto description = CreateSessionDescriptionWithBundleGroup();
544 EXPECT_TRUE(transport_controller_
545 ->SetLocalDescription(SdpType::kOffer, description.get())
546 .ok());
547
548 cricket::TransportStats stats;
549 EXPECT_TRUE(transport_controller_->GetStats(kAudioMid1, &stats));
550 EXPECT_EQ(kAudioMid1, stats.transport_name);
551 EXPECT_EQ(1u, stats.channel_stats.size());
552 // Return false for non-existing transport.
553 EXPECT_FALSE(transport_controller_->GetStats(kAudioMid2, &stats));
554}
555
556TEST_F(JsepTransportControllerTest, SignalConnectionStateFailed) {
557 CreateJsepTransportController(JsepTransportController::Config());
558 auto description = CreateSessionDescriptionWithoutBundle();
559 EXPECT_TRUE(transport_controller_
560 ->SetLocalDescription(SdpType::kOffer, description.get())
561 .ok());
562
563 auto fake_ice = static_cast<cricket::FakeIceTransport*>(
564 transport_controller_->GetDtlsTransport(kAudioMid1)->ice_transport());
565 fake_ice->SetCandidatesGatheringComplete();
566 fake_ice->SetConnectionCount(1);
567 // The connection stats will be failed if there is no active connection.
568 fake_ice->SetConnectionCount(0);
569 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
570 EXPECT_EQ(1, connection_state_signal_count_);
571}
572
573TEST_F(JsepTransportControllerTest, SignalConnectionStateConnected) {
574 CreateJsepTransportController(JsepTransportController::Config());
575 auto description = CreateSessionDescriptionWithoutBundle();
576 EXPECT_TRUE(transport_controller_
577 ->SetLocalDescription(SdpType::kOffer, description.get())
578 .ok());
579
580 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
581 transport_controller_->GetDtlsTransport(kAudioMid1));
582 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
583 transport_controller_->GetDtlsTransport(kVideoMid1));
584
585 // First, have one transport connect, and another fail, to ensure that
586 // the first transport connecting didn't trigger a "connected" state signal.
587 // We should only get a signal when all are connected.
588 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
589 fake_audio_dtls->SetWritable(true);
590 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
591 // Decrease the number of the connection to trigger the signal.
592 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
593 fake_video_dtls->fake_ice_transport()->SetConnectionCount(0);
594 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
595
596 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
597 EXPECT_EQ(1, connection_state_signal_count_);
598
599 // Set the connection count to be 2 and the cricket::FakeIceTransport will set
600 // the transport state to be STATE_CONNECTING.
601 fake_video_dtls->fake_ice_transport()->SetConnectionCount(2);
602 fake_video_dtls->SetWritable(true);
603 EXPECT_EQ_WAIT(cricket::kIceConnectionConnected, connection_state_, kTimeout);
604 EXPECT_EQ(2, connection_state_signal_count_);
605}
606
607TEST_F(JsepTransportControllerTest, SignalConnectionStateComplete) {
608 CreateJsepTransportController(JsepTransportController::Config());
609 auto description = CreateSessionDescriptionWithoutBundle();
610 EXPECT_TRUE(transport_controller_
611 ->SetLocalDescription(SdpType::kOffer, description.get())
612 .ok());
613
614 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
615 transport_controller_->GetDtlsTransport(kAudioMid1));
616 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
617 transport_controller_->GetDtlsTransport(kVideoMid1));
618
619 // First, have one transport connect, and another fail, to ensure that
620 // the first transport connecting didn't trigger a "connected" state signal.
621 // We should only get a signal when all are connected.
622 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
623 fake_audio_dtls->SetWritable(true);
624 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
625 // Decrease the number of the connection to trigger the signal.
626 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
627 fake_video_dtls->fake_ice_transport()->SetConnectionCount(0);
628 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
629
630 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
631 EXPECT_EQ(1, connection_state_signal_count_);
632
633 // Set the connection count to be 1 and the cricket::FakeIceTransport will set
634 // the transport state to be STATE_COMPLETED.
635 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
636 fake_video_dtls->SetWritable(true);
637 EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
638 EXPECT_EQ(2, connection_state_signal_count_);
639}
640
641TEST_F(JsepTransportControllerTest, SignalIceGatheringStateGathering) {
642 CreateJsepTransportController(JsepTransportController::Config());
643 auto description = CreateSessionDescriptionWithoutBundle();
644 EXPECT_TRUE(transport_controller_
645 ->SetLocalDescription(SdpType::kOffer, description.get())
646 .ok());
647
648 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
649 transport_controller_->GetDtlsTransport(kAudioMid1));
650 fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
651 // Should be in the gathering state as soon as any transport starts gathering.
652 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
653 EXPECT_EQ(1, gathering_state_signal_count_);
654}
655
656TEST_F(JsepTransportControllerTest, SignalIceGatheringStateComplete) {
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 fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
669 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
670 EXPECT_EQ(1, gathering_state_signal_count_);
671
672 // Have one transport finish gathering, to make sure gathering
673 // completion wasn't signalled if only one transport finished gathering.
674 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
675 EXPECT_EQ(1, gathering_state_signal_count_);
676
677 fake_video_dtls->fake_ice_transport()->MaybeStartGathering();
678 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
679 EXPECT_EQ(1, gathering_state_signal_count_);
680
681 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
682 EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
683 EXPECT_EQ(2, gathering_state_signal_count_);
684}
685
686// Test that when the last transport that hasn't finished connecting and/or
687// gathering is destroyed, the aggregate state jumps to "completed". This can
688// happen if, for example, we have an audio and video transport, the audio
689// transport completes, then we start bundling video on the audio transport.
690TEST_F(JsepTransportControllerTest,
691 SignalingWhenLastIncompleteTransportDestroyed) {
692 CreateJsepTransportController(JsepTransportController::Config());
693 auto description = CreateSessionDescriptionWithBundleGroup();
694 EXPECT_TRUE(transport_controller_
695 ->SetLocalDescription(SdpType::kOffer, description.get())
696 .ok());
697
698 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
699 transport_controller_->GetDtlsTransport(kAudioMid1));
700 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
701 transport_controller_->GetDtlsTransport(kVideoMid1));
702 EXPECT_NE(fake_audio_dtls, fake_video_dtls);
703
704 fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
705 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
706 EXPECT_EQ(1, gathering_state_signal_count_);
707
708 // Let the audio transport complete.
709 fake_audio_dtls->SetWritable(true);
710 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
711 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
712 EXPECT_EQ(1, gathering_state_signal_count_);
713
714 // Set the remote description and enable the bundle.
715 EXPECT_TRUE(transport_controller_
716 ->SetRemoteDescription(SdpType::kAnswer, description.get())
717 .ok());
718 // The BUNDLE should be enabled, the incomplete video transport should be
719 // deleted and the states shoud be updated.
720 fake_video_dtls = static_cast<FakeDtlsTransport*>(
721 transport_controller_->GetDtlsTransport(kVideoMid1));
722 EXPECT_EQ(fake_audio_dtls, fake_video_dtls);
723 EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
724 EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
725 EXPECT_EQ(2, gathering_state_signal_count_);
726}
727
728TEST_F(JsepTransportControllerTest, SignalCandidatesGathered) {
729 CreateJsepTransportController(JsepTransportController::Config());
730 auto description = CreateSessionDescriptionWithBundleGroup();
731 EXPECT_TRUE(transport_controller_
732 ->SetLocalDescription(SdpType::kOffer, description.get())
733 .ok());
734 transport_controller_->MaybeStartGathering();
735
736 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
737 transport_controller_->GetDtlsTransport(kAudioMid1));
738 fake_audio_dtls->fake_ice_transport()->SignalCandidateGathered(
739 fake_audio_dtls->fake_ice_transport(), CreateCandidate(kAudioMid1, 1));
740 EXPECT_EQ_WAIT(1, candidates_signal_count_, kTimeout);
741 EXPECT_EQ(1u, candidates_[kAudioMid1].size());
742}
743
744TEST_F(JsepTransportControllerTest, IceSignalingOccursOnSignalingThread) {
745 network_thread_ = rtc::Thread::CreateWithSocketServer();
746 network_thread_->Start();
747 CreateJsepTransportController(JsepTransportController::Config(),
748 signaling_thread_, network_thread_.get(),
749 /*PortAllocator=*/nullptr);
750 CreateLocalDescriptionAndCompleteConnectionOnNetworkThread();
751
752 // connecting --> connected --> completed
753 EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
754 EXPECT_EQ(2, connection_state_signal_count_);
755
756 // new --> gathering --> complete
757 EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
758 EXPECT_EQ(2, gathering_state_signal_count_);
759
760 EXPECT_EQ_WAIT(1u, candidates_[kAudioMid1].size(), kTimeout);
761 EXPECT_EQ_WAIT(1u, candidates_[kVideoMid1].size(), kTimeout);
762 EXPECT_EQ(2, candidates_signal_count_);
763
764 EXPECT_TRUE(!signaled_on_non_signaling_thread_);
765}
766
767// Older versions of Chrome expect the ICE role to be re-determined when an
768// ICE restart occurs, and also don't perform conflict resolution correctly,
769// so for now we can't safely stop doing this.
770// See: https://bugs.chromium.org/p/chromium/issues/detail?id=628676
771// TODO(deadbeef): Remove this when these old versions of Chrome reach a low
772// enough population.
773TEST_F(JsepTransportControllerTest, IceRoleRedeterminedOnIceRestartByDefault) {
774 CreateJsepTransportController(JsepTransportController::Config());
775 // Let the |transport_controller_| be the controlled side initially.
Karl Wiberg918f50c2018-07-05 11:40:33 +0200776 auto remote_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800777 AddAudioSection(remote_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
778 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
779 nullptr);
Karl Wiberg918f50c2018-07-05 11:40:33 +0200780 auto local_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800781 AddAudioSection(local_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
782 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
783 nullptr);
784
785 EXPECT_TRUE(transport_controller_
786 ->SetRemoteDescription(SdpType::kOffer, remote_offer.get())
787 .ok());
788 EXPECT_TRUE(transport_controller_
789 ->SetLocalDescription(SdpType::kAnswer, local_answer.get())
790 .ok());
791
792 auto fake_dtls = static_cast<FakeDtlsTransport*>(
793 transport_controller_->GetDtlsTransport(kAudioMid1));
794 EXPECT_EQ(cricket::ICEROLE_CONTROLLED,
795 fake_dtls->fake_ice_transport()->GetIceRole());
796
797 // New offer will trigger the ICE restart.
Karl Wiberg918f50c2018-07-05 11:40:33 +0200798 auto restart_local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800799 AddAudioSection(restart_local_offer.get(), kAudioMid1, kIceUfrag3, kIcePwd3,
800 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
801 nullptr);
802 EXPECT_TRUE(
803 transport_controller_
804 ->SetLocalDescription(SdpType::kOffer, restart_local_offer.get())
805 .ok());
806 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
807 fake_dtls->fake_ice_transport()->GetIceRole());
808}
809
810// Test that if the TransportController was created with the
811// |redetermine_role_on_ice_restart| parameter set to false, the role is *not*
812// redetermined on an ICE restart.
813TEST_F(JsepTransportControllerTest, IceRoleNotRedetermined) {
814 JsepTransportController::Config config;
815 config.redetermine_role_on_ice_restart = false;
816
817 CreateJsepTransportController(config);
818 // Let the |transport_controller_| be the controlled side initially.
Karl Wiberg918f50c2018-07-05 11:40:33 +0200819 auto remote_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800820 AddAudioSection(remote_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
821 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
822 nullptr);
Karl Wiberg918f50c2018-07-05 11:40:33 +0200823 auto local_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800824 AddAudioSection(local_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
825 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
826 nullptr);
827
828 EXPECT_TRUE(transport_controller_
829 ->SetRemoteDescription(SdpType::kOffer, remote_offer.get())
830 .ok());
831 EXPECT_TRUE(transport_controller_
832 ->SetLocalDescription(SdpType::kAnswer, local_answer.get())
833 .ok());
834
835 auto fake_dtls = static_cast<FakeDtlsTransport*>(
836 transport_controller_->GetDtlsTransport(kAudioMid1));
837 EXPECT_EQ(cricket::ICEROLE_CONTROLLED,
838 fake_dtls->fake_ice_transport()->GetIceRole());
839
840 // New offer will trigger the ICE restart.
Karl Wiberg918f50c2018-07-05 11:40:33 +0200841 auto restart_local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800842 AddAudioSection(restart_local_offer.get(), kAudioMid1, kIceUfrag3, kIcePwd3,
843 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
844 nullptr);
845 EXPECT_TRUE(
846 transport_controller_
847 ->SetLocalDescription(SdpType::kOffer, restart_local_offer.get())
848 .ok());
849 EXPECT_EQ(cricket::ICEROLE_CONTROLLED,
850 fake_dtls->fake_ice_transport()->GetIceRole());
851}
852
853// Tests ICE-Lite mode in remote answer.
854TEST_F(JsepTransportControllerTest, SetIceRoleWhenIceLiteInRemoteAnswer) {
855 CreateJsepTransportController(JsepTransportController::Config());
Karl Wiberg918f50c2018-07-05 11:40:33 +0200856 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800857 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
858 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
859 nullptr);
860 EXPECT_TRUE(transport_controller_
861 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
862 .ok());
863 auto fake_dtls = static_cast<FakeDtlsTransport*>(
864 transport_controller_->GetDtlsTransport(kAudioMid1));
865 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
866 fake_dtls->fake_ice_transport()->GetIceRole());
867 EXPECT_EQ(cricket::ICEMODE_FULL,
868 fake_dtls->fake_ice_transport()->remote_ice_mode());
869
Karl Wiberg918f50c2018-07-05 11:40:33 +0200870 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800871 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
872 cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_PASSIVE,
873 nullptr);
874 EXPECT_TRUE(transport_controller_
875 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
876 .ok());
877 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
878 fake_dtls->fake_ice_transport()->GetIceRole());
879 EXPECT_EQ(cricket::ICEMODE_LITE,
880 fake_dtls->fake_ice_transport()->remote_ice_mode());
881}
882
883// Tests that the ICE role remains "controlling" if a subsequent offer that
884// does an ICE restart is received from an ICE lite endpoint. Regression test
885// for: https://crbug.com/710760
886TEST_F(JsepTransportControllerTest,
887 IceRoleIsControllingAfterIceRestartFromIceLiteEndpoint) {
888 CreateJsepTransportController(JsepTransportController::Config());
Karl Wiberg918f50c2018-07-05 11:40:33 +0200889 auto remote_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800890 AddAudioSection(remote_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
891 cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_ACTPASS,
892 nullptr);
Karl Wiberg918f50c2018-07-05 11:40:33 +0200893 auto local_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800894 AddAudioSection(local_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
895 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
896 nullptr);
897 // Initial Offer/Answer exchange. If the remote offerer is ICE-Lite, then the
898 // local side is the controlling.
899 EXPECT_TRUE(transport_controller_
900 ->SetRemoteDescription(SdpType::kOffer, remote_offer.get())
901 .ok());
902 EXPECT_TRUE(transport_controller_
903 ->SetLocalDescription(SdpType::kAnswer, local_answer.get())
904 .ok());
905 auto fake_dtls = static_cast<FakeDtlsTransport*>(
906 transport_controller_->GetDtlsTransport(kAudioMid1));
907 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
908 fake_dtls->fake_ice_transport()->GetIceRole());
909
910 // In the subsequence remote offer triggers an ICE restart.
Karl Wiberg918f50c2018-07-05 11:40:33 +0200911 auto remote_offer2 = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800912 AddAudioSection(remote_offer2.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
913 cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_ACTPASS,
914 nullptr);
Karl Wiberg918f50c2018-07-05 11:40:33 +0200915 auto local_answer2 = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800916 AddAudioSection(local_answer2.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
917 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
918 nullptr);
919 EXPECT_TRUE(transport_controller_
920 ->SetRemoteDescription(SdpType::kOffer, remote_offer2.get())
921 .ok());
922 EXPECT_TRUE(transport_controller_
923 ->SetLocalDescription(SdpType::kAnswer, local_answer2.get())
924 .ok());
925 fake_dtls = static_cast<FakeDtlsTransport*>(
926 transport_controller_->GetDtlsTransport(kAudioMid1));
927 // The local side is still the controlling role since the remote side is using
928 // ICE-Lite.
929 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
930 fake_dtls->fake_ice_transport()->GetIceRole());
931}
932
933// Tests that the SDP has more than one audio/video m= sections.
934TEST_F(JsepTransportControllerTest, MultipleMediaSectionsOfSameTypeWithBundle) {
935 CreateJsepTransportController(JsepTransportController::Config());
936 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
937 bundle_group.AddContentName(kAudioMid1);
938 bundle_group.AddContentName(kAudioMid2);
939 bundle_group.AddContentName(kVideoMid1);
940 bundle_group.AddContentName(kDataMid1);
941
Karl Wiberg918f50c2018-07-05 11:40:33 +0200942 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800943 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
944 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
945 nullptr);
946 AddAudioSection(local_offer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
947 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
948 nullptr);
949 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
950 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
951 nullptr);
952 AddDataSection(local_offer.get(), kDataMid1,
953 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
954 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
955 nullptr);
956
Karl Wiberg918f50c2018-07-05 11:40:33 +0200957 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800958 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
959 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
960 nullptr);
961 AddAudioSection(remote_answer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
962 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
963 nullptr);
964 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
965 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
966 nullptr);
967 AddDataSection(remote_answer.get(), kDataMid1,
968 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
969 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
970 nullptr);
971
972 local_offer->AddGroup(bundle_group);
973 remote_answer->AddGroup(bundle_group);
974
975 EXPECT_TRUE(transport_controller_
976 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
977 .ok());
978 EXPECT_TRUE(transport_controller_
979 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
980 .ok());
981 // Verify that all the sections are bundled on kAudio1.
982 auto transport1 = transport_controller_->GetRtpTransport(kAudioMid1);
983 auto transport2 = transport_controller_->GetRtpTransport(kAudioMid2);
984 auto transport3 = transport_controller_->GetRtpTransport(kVideoMid1);
985 auto transport4 = transport_controller_->GetRtpTransport(kDataMid1);
986 EXPECT_EQ(transport1, transport2);
987 EXPECT_EQ(transport1, transport3);
988 EXPECT_EQ(transport1, transport4);
989
990 // Verify the OnRtpTransport/DtlsTransportChanged signals are fired correctly.
991 auto it = changed_rtp_transport_by_mid_.find(kAudioMid2);
992 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
993 EXPECT_EQ(transport1, it->second);
994 it = changed_rtp_transport_by_mid_.find(kAudioMid2);
995 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
996 EXPECT_EQ(transport1, it->second);
997 it = changed_rtp_transport_by_mid_.find(kVideoMid1);
998 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
999 EXPECT_EQ(transport1, it->second);
1000 // Verify the DtlsTransport for the SCTP data channel is reset correctly.
1001 auto it2 = changed_dtls_transport_by_mid_.find(kDataMid1);
1002 ASSERT_TRUE(it2 != changed_dtls_transport_by_mid_.end());
1003 EXPECT_EQ(transport1->rtp_packet_transport(), it2->second);
1004}
1005
1006// Tests that only a subset of all the m= sections are bundled.
1007TEST_F(JsepTransportControllerTest, BundleSubsetOfMediaSections) {
1008 CreateJsepTransportController(JsepTransportController::Config());
1009 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1010 bundle_group.AddContentName(kAudioMid1);
1011 bundle_group.AddContentName(kVideoMid1);
1012
Karl Wiberg918f50c2018-07-05 11:40:33 +02001013 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001014 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1015 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1016 nullptr);
1017 AddAudioSection(local_offer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
1018 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1019 nullptr);
1020 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1021 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1022 nullptr);
1023
Karl Wiberg918f50c2018-07-05 11:40:33 +02001024 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001025 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1026 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1027 nullptr);
1028 AddAudioSection(remote_answer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
1029 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1030 nullptr);
1031 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1032 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1033 nullptr);
1034
1035 local_offer->AddGroup(bundle_group);
1036 remote_answer->AddGroup(bundle_group);
1037 EXPECT_TRUE(transport_controller_
1038 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1039 .ok());
1040 EXPECT_TRUE(transport_controller_
1041 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1042 .ok());
1043
1044 // Verifiy that only |kAudio1| and |kVideo1| are bundled.
1045 auto transport1 = transport_controller_->GetRtpTransport(kAudioMid1);
1046 auto transport2 = transport_controller_->GetRtpTransport(kAudioMid2);
1047 auto transport3 = transport_controller_->GetRtpTransport(kVideoMid1);
1048 EXPECT_NE(transport1, transport2);
1049 EXPECT_EQ(transport1, transport3);
1050
1051 auto it = changed_rtp_transport_by_mid_.find(kVideoMid1);
1052 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1053 EXPECT_EQ(transport1, it->second);
1054 it = changed_rtp_transport_by_mid_.find(kAudioMid2);
Zhi Huangd2248f82018-04-10 14:41:03 -07001055 EXPECT_TRUE(transport2 == it->second);
Zhi Huange818b6e2018-02-22 15:26:27 -08001056}
1057
1058// Tests that the initial offer/answer only have data section and audio/video
1059// sections are added in the subsequent offer.
1060TEST_F(JsepTransportControllerTest, BundleOnDataSectionInSubsequentOffer) {
1061 CreateJsepTransportController(JsepTransportController::Config());
1062 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1063 bundle_group.AddContentName(kDataMid1);
1064
Karl Wiberg918f50c2018-07-05 11:40:33 +02001065 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001066 AddDataSection(local_offer.get(), kDataMid1,
1067 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
1068 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1069 nullptr);
Karl Wiberg918f50c2018-07-05 11:40:33 +02001070 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001071 AddDataSection(remote_answer.get(), kDataMid1,
1072 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
1073 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1074 nullptr);
1075 local_offer->AddGroup(bundle_group);
1076 remote_answer->AddGroup(bundle_group);
1077
1078 EXPECT_TRUE(transport_controller_
1079 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1080 .ok());
1081 EXPECT_TRUE(transport_controller_
1082 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1083 .ok());
1084 auto data_transport = transport_controller_->GetRtpTransport(kDataMid1);
1085
1086 // Add audio/video sections in subsequent offer.
1087 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1088 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1089 nullptr);
1090 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1091 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1092 nullptr);
1093 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1094 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1095 nullptr);
1096 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1097 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1098 nullptr);
1099
1100 // Reset the bundle group and do another offer/answer exchange.
1101 bundle_group.AddContentName(kAudioMid1);
1102 bundle_group.AddContentName(kVideoMid1);
1103 local_offer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1104 remote_answer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1105 local_offer->AddGroup(bundle_group);
1106 remote_answer->AddGroup(bundle_group);
1107
1108 EXPECT_TRUE(transport_controller_
1109 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1110 .ok());
1111 EXPECT_TRUE(transport_controller_
1112 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1113 .ok());
1114
1115 auto audio_transport = transport_controller_->GetRtpTransport(kAudioMid1);
1116 auto video_transport = transport_controller_->GetRtpTransport(kVideoMid1);
1117 EXPECT_EQ(data_transport, audio_transport);
1118 EXPECT_EQ(data_transport, video_transport);
1119}
1120
1121TEST_F(JsepTransportControllerTest, VideoDataRejectedInAnswer) {
1122 CreateJsepTransportController(JsepTransportController::Config());
1123 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1124 bundle_group.AddContentName(kAudioMid1);
1125 bundle_group.AddContentName(kVideoMid1);
1126 bundle_group.AddContentName(kDataMid1);
1127
Karl Wiberg918f50c2018-07-05 11:40:33 +02001128 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001129 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1130 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1131 nullptr);
1132 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1133 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1134 nullptr);
1135 AddDataSection(local_offer.get(), kDataMid1,
1136 cricket::MediaProtocolType::kSctp, 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 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1145 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1146 nullptr);
1147 AddDataSection(remote_answer.get(), kDataMid1,
1148 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1149 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1150 nullptr);
1151 // Reject video and data section.
1152 remote_answer->contents()[1].rejected = true;
1153 remote_answer->contents()[2].rejected = true;
1154
1155 local_offer->AddGroup(bundle_group);
1156 remote_answer->AddGroup(bundle_group);
1157
1158 EXPECT_TRUE(transport_controller_
1159 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1160 .ok());
1161 EXPECT_TRUE(transport_controller_
1162 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1163 .ok());
1164
1165 // Verify the RtpTransport/DtlsTransport is destroyed correctly.
1166 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kVideoMid1));
1167 EXPECT_EQ(nullptr, transport_controller_->GetDtlsTransport(kDataMid1));
1168 // Verify the signals are fired correctly.
1169 auto it = changed_rtp_transport_by_mid_.find(kVideoMid1);
1170 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1171 EXPECT_EQ(nullptr, it->second);
1172 auto it2 = changed_dtls_transport_by_mid_.find(kDataMid1);
1173 ASSERT_TRUE(it2 != changed_dtls_transport_by_mid_.end());
1174 EXPECT_EQ(nullptr, it2->second);
1175}
1176
1177// Tests that changing the bundled MID in subsequent offer/answer exchange is
1178// not supported.
1179// TODO(bugs.webrtc.org/6704): Change this test to expect success once issue is
1180// fixed
1181TEST_F(JsepTransportControllerTest, ChangeBundledMidNotSupported) {
1182 CreateJsepTransportController(JsepTransportController::Config());
1183 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1184 bundle_group.AddContentName(kAudioMid1);
1185 bundle_group.AddContentName(kVideoMid1);
1186
Karl Wiberg918f50c2018-07-05 11:40:33 +02001187 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001188 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1189 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1190 nullptr);
1191 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1192 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1193 nullptr);
1194
Karl Wiberg918f50c2018-07-05 11:40:33 +02001195 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001196 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1197 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1198 nullptr);
1199 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1200 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1201 nullptr);
1202
1203 local_offer->AddGroup(bundle_group);
1204 remote_answer->AddGroup(bundle_group);
1205 EXPECT_TRUE(transport_controller_
1206 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1207 .ok());
1208 EXPECT_TRUE(transport_controller_
1209 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1210 .ok());
1211 EXPECT_EQ(transport_controller_->GetRtpTransport(kAudioMid1),
1212 transport_controller_->GetRtpTransport(kVideoMid1));
1213
1214 // Reorder the bundle group.
1215 EXPECT_TRUE(bundle_group.RemoveContentName(kAudioMid1));
1216 bundle_group.AddContentName(kAudioMid1);
1217 // The answerer uses the new bundle group and now the bundle mid is changed to
1218 // |kVideo1|.
1219 remote_answer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1220 remote_answer->AddGroup(bundle_group);
1221 EXPECT_TRUE(transport_controller_
1222 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1223 .ok());
1224 EXPECT_FALSE(transport_controller_
1225 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1226 .ok());
1227}
Zhi Huange830e682018-03-30 10:48:35 -07001228// Test that rejecting only the first m= section of a BUNDLE group is treated as
1229// an error, but rejecting all of them works as expected.
1230TEST_F(JsepTransportControllerTest, RejectFirstContentInBundleGroup) {
1231 CreateJsepTransportController(JsepTransportController::Config());
1232 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1233 bundle_group.AddContentName(kAudioMid1);
1234 bundle_group.AddContentName(kVideoMid1);
1235 bundle_group.AddContentName(kDataMid1);
1236
Karl Wiberg918f50c2018-07-05 11:40:33 +02001237 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange830e682018-03-30 10:48:35 -07001238 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1239 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1240 nullptr);
1241 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1242 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1243 nullptr);
1244 AddDataSection(local_offer.get(), kDataMid1,
1245 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1246 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1247 nullptr);
1248
Karl Wiberg918f50c2018-07-05 11:40:33 +02001249 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange830e682018-03-30 10:48:35 -07001250 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1251 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1252 nullptr);
1253 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1254 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1255 nullptr);
1256 AddDataSection(remote_answer.get(), kDataMid1,
1257 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1258 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1259 nullptr);
1260 // Reject audio content in answer.
1261 remote_answer->contents()[0].rejected = true;
1262
1263 local_offer->AddGroup(bundle_group);
1264 remote_answer->AddGroup(bundle_group);
1265
1266 EXPECT_TRUE(transport_controller_
1267 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1268 .ok());
1269 EXPECT_FALSE(transport_controller_
1270 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1271 .ok());
1272
1273 // Reject all the contents.
1274 remote_answer->contents()[1].rejected = true;
1275 remote_answer->contents()[2].rejected = true;
1276 EXPECT_TRUE(transport_controller_
1277 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1278 .ok());
1279 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kAudioMid1));
1280 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kVideoMid1));
1281 EXPECT_EQ(nullptr, transport_controller_->GetDtlsTransport(kDataMid1));
1282}
1283
1284// Tests that applying non-RTCP-mux offer would fail when kRtcpMuxPolicyRequire
1285// is used.
1286TEST_F(JsepTransportControllerTest, ApplyNonRtcpMuxOfferWhenMuxingRequired) {
1287 JsepTransportController::Config config;
1288 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
1289 CreateJsepTransportController(config);
Karl Wiberg918f50c2018-07-05 11:40:33 +02001290 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange830e682018-03-30 10:48:35 -07001291 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1292 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1293 nullptr);
1294
1295 local_offer->contents()[0].media_description()->set_rtcp_mux(false);
1296 // Applying a non-RTCP-mux offer is expected to fail.
1297 EXPECT_FALSE(transport_controller_
1298 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1299 .ok());
1300}
1301
1302// Tests that applying non-RTCP-mux answer would fail when kRtcpMuxPolicyRequire
1303// is used.
1304TEST_F(JsepTransportControllerTest, ApplyNonRtcpMuxAnswerWhenMuxingRequired) {
1305 JsepTransportController::Config config;
1306 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
1307 CreateJsepTransportController(config);
Karl Wiberg918f50c2018-07-05 11:40:33 +02001308 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange830e682018-03-30 10:48:35 -07001309 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1310 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1311 nullptr);
1312 EXPECT_TRUE(transport_controller_
1313 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1314 .ok());
1315
Karl Wiberg918f50c2018-07-05 11:40:33 +02001316 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange830e682018-03-30 10:48:35 -07001317 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1318 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1319 nullptr);
1320 // Applying a non-RTCP-mux answer is expected to fail.
1321 remote_answer->contents()[0].media_description()->set_rtcp_mux(false);
1322 EXPECT_FALSE(transport_controller_
1323 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1324 .ok());
1325}
Zhi Huange818b6e2018-02-22 15:26:27 -08001326
Zhi Huangd2248f82018-04-10 14:41:03 -07001327// This tests that the BUNDLE group in answer should be a subset of the offered
1328// group.
1329TEST_F(JsepTransportControllerTest,
1330 AddContentToBundleGroupInAnswerNotSupported) {
1331 CreateJsepTransportController(JsepTransportController::Config());
1332 auto local_offer = CreateSessionDescriptionWithoutBundle();
1333 auto remote_answer = CreateSessionDescriptionWithoutBundle();
1334
1335 cricket::ContentGroup offer_bundle_group(cricket::GROUP_TYPE_BUNDLE);
1336 offer_bundle_group.AddContentName(kAudioMid1);
1337 local_offer->AddGroup(offer_bundle_group);
1338
1339 cricket::ContentGroup answer_bundle_group(cricket::GROUP_TYPE_BUNDLE);
1340 answer_bundle_group.AddContentName(kAudioMid1);
1341 answer_bundle_group.AddContentName(kVideoMid1);
1342 remote_answer->AddGroup(answer_bundle_group);
1343 EXPECT_TRUE(transport_controller_
1344 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1345 .ok());
1346 EXPECT_FALSE(transport_controller_
1347 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1348 .ok());
1349}
1350
1351// This tests that the BUNDLE group with non-existing MID should be rejectd.
1352TEST_F(JsepTransportControllerTest, RejectBundleGroupWithNonExistingMid) {
1353 CreateJsepTransportController(JsepTransportController::Config());
1354 auto local_offer = CreateSessionDescriptionWithoutBundle();
1355 auto remote_answer = CreateSessionDescriptionWithoutBundle();
1356
1357 cricket::ContentGroup invalid_bundle_group(cricket::GROUP_TYPE_BUNDLE);
1358 // The BUNDLE group is invalid because there is no data section in the
1359 // description.
1360 invalid_bundle_group.AddContentName(kDataMid1);
1361 local_offer->AddGroup(invalid_bundle_group);
1362 remote_answer->AddGroup(invalid_bundle_group);
1363
1364 EXPECT_FALSE(transport_controller_
1365 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1366 .ok());
1367 EXPECT_FALSE(transport_controller_
1368 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1369 .ok());
1370}
1371
1372// This tests that an answer shouldn't be able to remove an m= section from an
1373// established group without rejecting it.
1374TEST_F(JsepTransportControllerTest, RemoveContentFromBundleGroup) {
1375 CreateJsepTransportController(JsepTransportController::Config());
1376
1377 auto local_offer = CreateSessionDescriptionWithBundleGroup();
1378 auto remote_answer = CreateSessionDescriptionWithBundleGroup();
1379 EXPECT_TRUE(transport_controller_
1380 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1381 .ok());
1382 EXPECT_TRUE(transport_controller_
1383 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1384 .ok());
1385
1386 // Do an re-offer/answer.
1387 EXPECT_TRUE(transport_controller_
1388 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1389 .ok());
1390 auto new_answer = CreateSessionDescriptionWithoutBundle();
1391 cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
1392 // The answer removes video from the BUNDLE group without rejecting it is
1393 // invalid.
1394 new_bundle_group.AddContentName(kAudioMid1);
1395 new_answer->AddGroup(new_bundle_group);
1396
1397 // Applying invalid answer is expected to fail.
1398 EXPECT_FALSE(transport_controller_
1399 ->SetRemoteDescription(SdpType::kAnswer, new_answer.get())
1400 .ok());
1401
1402 // Rejected the video content.
1403 auto video_content = new_answer->GetContentByName(kVideoMid1);
1404 ASSERT_TRUE(video_content);
1405 video_content->rejected = true;
1406 EXPECT_TRUE(transport_controller_
1407 ->SetRemoteDescription(SdpType::kAnswer, new_answer.get())
1408 .ok());
1409}
1410
Zhi Huange818b6e2018-02-22 15:26:27 -08001411} // namespace webrtc