blob: 129d22a4fcc84730e1f99b7a854e967ca2ef9a0f [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
Piotr (Peter) Slatalacc8e8bb2018-11-15 08:26:19 -080014#include "api/media_transport_interface.h"
Anton Sukhanov7940da02018-10-10 10:34:49 -070015#include "api/test/fake_media_transport.h"
Zhi Huange818b6e2018-02-22 15:26:27 -080016#include "p2p/base/fakedtlstransport.h"
17#include "p2p/base/fakeicetransport.h"
18#include "p2p/base/transportfactoryinterface.h"
19#include "p2p/base/transportinfo.h"
20#include "pc/jseptransportcontroller.h"
21#include "rtc_base/gunit.h"
Zhi Huange818b6e2018-02-22 15:26:27 -080022#include "rtc_base/thread.h"
23#include "test/gtest.h"
24
25using cricket::FakeDtlsTransport;
26using cricket::Candidate;
27using cricket::Candidates;
28using webrtc::SdpType;
29
30static const int kTimeout = 100;
31static const char kIceUfrag1[] = "u0001";
32static const char kIcePwd1[] = "TESTICEPWD00000000000001";
33static const char kIceUfrag2[] = "u0002";
34static const char kIcePwd2[] = "TESTICEPWD00000000000002";
35static const char kIceUfrag3[] = "u0003";
36static const char kIcePwd3[] = "TESTICEPWD00000000000003";
37static const char kAudioMid1[] = "audio1";
38static const char kAudioMid2[] = "audio2";
39static const char kVideoMid1[] = "video1";
40static const char kVideoMid2[] = "video2";
41static const char kDataMid1[] = "data1";
42
43namespace webrtc {
44
Piotr (Peter) Slatala9f956252018-10-31 08:25:26 -070045namespace {
46
47// Media transport factory requires crypto settings to be present in order to
48// create media transport.
49void AddCryptoSettings(cricket::SessionDescription* description) {
50 for (auto& content : description->contents()) {
51 content.media_description()->AddCrypto(cricket::CryptoParams(
52 /*t=*/0, std::string(rtc::CS_AES_CM_128_HMAC_SHA1_80),
53 "inline:YUJDZGVmZ2hpSktMbW9QUXJzVHVWd3l6MTIzNDU2", ""));
54 }
55}
56
57} // namespace
58
Zhi Huange818b6e2018-02-22 15:26:27 -080059class FakeTransportFactory : public cricket::TransportFactoryInterface {
60 public:
61 std::unique_ptr<cricket::IceTransportInternal> CreateIceTransport(
62 const std::string& transport_name,
63 int component) override {
Karl Wiberg918f50c2018-07-05 11:40:33 +020064 return absl::make_unique<cricket::FakeIceTransport>(transport_name,
65 component);
Zhi Huange818b6e2018-02-22 15:26:27 -080066 }
67
68 std::unique_ptr<cricket::DtlsTransportInternal> CreateDtlsTransport(
69 std::unique_ptr<cricket::IceTransportInternal> ice,
Benjamin Wrighta54daf12018-10-11 15:33:17 -070070 const webrtc::CryptoOptions& crypto_options) override {
Zhi Huange818b6e2018-02-22 15:26:27 -080071 std::unique_ptr<cricket::FakeIceTransport> fake_ice(
72 static_cast<cricket::FakeIceTransport*>(ice.release()));
Karl Wiberg918f50c2018-07-05 11:40:33 +020073 return absl::make_unique<FakeDtlsTransport>(std::move(fake_ice));
Zhi Huange818b6e2018-02-22 15:26:27 -080074 }
75};
76
Zhi Huang365381f2018-04-13 16:44:34 -070077class JsepTransportControllerTest : public JsepTransportController::Observer,
78 public testing::Test,
Zhi Huange818b6e2018-02-22 15:26:27 -080079 public sigslot::has_slots<> {
80 public:
81 JsepTransportControllerTest() : signaling_thread_(rtc::Thread::Current()) {
Karl Wiberg918f50c2018-07-05 11:40:33 +020082 fake_transport_factory_ = absl::make_unique<FakeTransportFactory>();
Zhi Huange818b6e2018-02-22 15:26:27 -080083 }
84
85 void CreateJsepTransportController(
86 JsepTransportController::Config config,
87 rtc::Thread* signaling_thread = rtc::Thread::Current(),
88 rtc::Thread* network_thread = rtc::Thread::Current(),
89 cricket::PortAllocator* port_allocator = nullptr) {
Zhi Huang365381f2018-04-13 16:44:34 -070090 config.transport_observer = this;
Zhi Huange818b6e2018-02-22 15:26:27 -080091 // The tests only works with |fake_transport_factory|;
92 config.external_transport_factory = fake_transport_factory_.get();
Zach Steine20867f2018-08-02 13:20:15 -070093 // TODO(zstein): Provide an AsyncResolverFactory once it is required.
Karl Wiberg918f50c2018-07-05 11:40:33 +020094 transport_controller_ = absl::make_unique<JsepTransportController>(
Zach Steine20867f2018-08-02 13:20:15 -070095 signaling_thread, network_thread, port_allocator, nullptr, config);
Zhi Huange818b6e2018-02-22 15:26:27 -080096 ConnectTransportControllerSignals();
97 }
98
99 void ConnectTransportControllerSignals() {
100 transport_controller_->SignalIceConnectionState.connect(
101 this, &JsepTransportControllerTest::OnConnectionState);
Jonas Olsson635474e2018-10-18 15:58:17 +0200102 transport_controller_->SignalStandardizedIceConnectionState.connect(
103 this, &JsepTransportControllerTest::OnStandardizedIceConnectionState);
104 transport_controller_->SignalConnectionState.connect(
105 this, &JsepTransportControllerTest::OnCombinedConnectionState);
Zhi Huange818b6e2018-02-22 15:26:27 -0800106 transport_controller_->SignalIceGatheringState.connect(
107 this, &JsepTransportControllerTest::OnGatheringState);
108 transport_controller_->SignalIceCandidatesGathered.connect(
109 this, &JsepTransportControllerTest::OnCandidatesGathered);
Zhi Huange818b6e2018-02-22 15:26:27 -0800110 }
111
112 std::unique_ptr<cricket::SessionDescription>
113 CreateSessionDescriptionWithoutBundle() {
Karl Wiberg918f50c2018-07-05 11:40:33 +0200114 auto description = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800115 AddAudioSection(description.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
116 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
117 nullptr);
118 AddVideoSection(description.get(), kVideoMid1, kIceUfrag1, kIcePwd1,
119 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
120 nullptr);
121 return description;
122 }
123
124 std::unique_ptr<cricket::SessionDescription>
125 CreateSessionDescriptionWithBundleGroup() {
126 auto description = CreateSessionDescriptionWithoutBundle();
127 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
128 bundle_group.AddContentName(kAudioMid1);
129 bundle_group.AddContentName(kVideoMid1);
130 description->AddGroup(bundle_group);
131
132 return description;
133 }
134
135 void AddAudioSection(cricket::SessionDescription* description,
136 const std::string& mid,
137 const std::string& ufrag,
138 const std::string& pwd,
139 cricket::IceMode ice_mode,
140 cricket::ConnectionRole conn_role,
141 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
142 std::unique_ptr<cricket::AudioContentDescription> audio(
143 new cricket::AudioContentDescription());
Zhi Huange830e682018-03-30 10:48:35 -0700144 // Set RTCP-mux to be true because the default policy is "mux required".
145 audio->set_rtcp_mux(true);
Zhi Huange818b6e2018-02-22 15:26:27 -0800146 description->AddContent(mid, cricket::MediaProtocolType::kRtp,
147 /*rejected=*/false, audio.release());
148 AddTransportInfo(description, mid, ufrag, pwd, ice_mode, conn_role, cert);
149 }
150
151 void AddVideoSection(cricket::SessionDescription* description,
152 const std::string& mid,
153 const std::string& ufrag,
154 const std::string& pwd,
155 cricket::IceMode ice_mode,
156 cricket::ConnectionRole conn_role,
157 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
158 std::unique_ptr<cricket::VideoContentDescription> video(
159 new cricket::VideoContentDescription());
Zhi Huange830e682018-03-30 10:48:35 -0700160 // Set RTCP-mux to be true because the default policy is "mux required".
161 video->set_rtcp_mux(true);
Zhi Huange818b6e2018-02-22 15:26:27 -0800162 description->AddContent(mid, cricket::MediaProtocolType::kRtp,
163 /*rejected=*/false, video.release());
164 AddTransportInfo(description, mid, ufrag, pwd, ice_mode, conn_role, cert);
165 }
166
167 void AddDataSection(cricket::SessionDescription* description,
168 const std::string& mid,
169 cricket::MediaProtocolType protocol_type,
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<cricket::DataContentDescription> data(
176 new cricket::DataContentDescription());
Zhi Huange830e682018-03-30 10:48:35 -0700177 data->set_rtcp_mux(true);
Zhi Huange818b6e2018-02-22 15:26:27 -0800178 description->AddContent(mid, protocol_type,
179 /*rejected=*/false, data.release());
180 AddTransportInfo(description, mid, ufrag, pwd, ice_mode, conn_role, cert);
181 }
182
183 void AddTransportInfo(cricket::SessionDescription* description,
184 const std::string& mid,
185 const std::string& ufrag,
186 const std::string& pwd,
187 cricket::IceMode ice_mode,
188 cricket::ConnectionRole conn_role,
189 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
190 std::unique_ptr<rtc::SSLFingerprint> fingerprint;
191 if (cert) {
Steve Anton4905edb2018-10-15 19:27:44 -0700192 fingerprint = rtc::SSLFingerprint::CreateFromCertificate(*cert);
Zhi Huange818b6e2018-02-22 15:26:27 -0800193 }
194
195 cricket::TransportDescription transport_desc(std::vector<std::string>(),
196 ufrag, pwd, ice_mode,
197 conn_role, fingerprint.get());
198 description->AddTransportInfo(cricket::TransportInfo(mid, transport_desc));
199 }
200
201 cricket::IceConfig CreateIceConfig(
202 int receiving_timeout,
203 cricket::ContinualGatheringPolicy continual_gathering_policy) {
204 cricket::IceConfig config;
205 config.receiving_timeout = receiving_timeout;
206 config.continual_gathering_policy = continual_gathering_policy;
207 return config;
208 }
209
210 Candidate CreateCandidate(const std::string& transport_name, int component) {
211 Candidate c;
212 c.set_transport_name(transport_name);
213 c.set_address(rtc::SocketAddress("192.168.1.1", 8000));
214 c.set_component(component);
215 c.set_protocol(cricket::UDP_PROTOCOL_NAME);
216 c.set_priority(1);
217 return c;
218 }
219
220 void CreateLocalDescriptionAndCompleteConnectionOnNetworkThread() {
221 if (!network_thread_->IsCurrent()) {
222 network_thread_->Invoke<void>(RTC_FROM_HERE, [&] {
223 CreateLocalDescriptionAndCompleteConnectionOnNetworkThread();
224 });
225 return;
226 }
227
228 auto description = CreateSessionDescriptionWithBundleGroup();
229 EXPECT_TRUE(transport_controller_
230 ->SetLocalDescription(SdpType::kOffer, description.get())
231 .ok());
232
233 transport_controller_->MaybeStartGathering();
234 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
235 transport_controller_->GetDtlsTransport(kAudioMid1));
236 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
237 transport_controller_->GetDtlsTransport(kVideoMid1));
238 fake_audio_dtls->fake_ice_transport()->SignalCandidateGathered(
239 fake_audio_dtls->fake_ice_transport(),
240 CreateCandidate(kAudioMid1, /*component=*/1));
241 fake_video_dtls->fake_ice_transport()->SignalCandidateGathered(
242 fake_video_dtls->fake_ice_transport(),
243 CreateCandidate(kVideoMid1, /*component=*/1));
244 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
245 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
246 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(2);
247 fake_video_dtls->fake_ice_transport()->SetConnectionCount(2);
248 fake_audio_dtls->SetReceiving(true);
249 fake_video_dtls->SetReceiving(true);
250 fake_audio_dtls->SetWritable(true);
251 fake_video_dtls->SetWritable(true);
252 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
253 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
254 }
255
256 protected:
257 void OnConnectionState(cricket::IceConnectionState state) {
258 if (!signaling_thread_->IsCurrent()) {
259 signaled_on_non_signaling_thread_ = true;
260 }
261 connection_state_ = state;
262 ++connection_state_signal_count_;
263 }
264
Jonas Olsson635474e2018-10-18 15:58:17 +0200265 void OnStandardizedIceConnectionState(
266 PeerConnectionInterface::IceConnectionState state) {
267 if (!signaling_thread_->IsCurrent()) {
268 signaled_on_non_signaling_thread_ = true;
269 }
270 ice_connection_state_ = state;
271 ++ice_connection_state_signal_count_;
272 }
273
274 void OnCombinedConnectionState(
275 PeerConnectionInterface::PeerConnectionState state) {
276 if (!signaling_thread_->IsCurrent()) {
277 signaled_on_non_signaling_thread_ = true;
278 }
279 combined_connection_state_ = state;
280 ++combined_connection_state_signal_count_;
281 }
282
Zhi Huange818b6e2018-02-22 15:26:27 -0800283 void OnGatheringState(cricket::IceGatheringState state) {
284 if (!signaling_thread_->IsCurrent()) {
285 signaled_on_non_signaling_thread_ = true;
286 }
287 gathering_state_ = state;
288 ++gathering_state_signal_count_;
289 }
290
291 void OnCandidatesGathered(const std::string& transport_name,
292 const Candidates& candidates) {
293 if (!signaling_thread_->IsCurrent()) {
294 signaled_on_non_signaling_thread_ = true;
295 }
296 candidates_[transport_name].insert(candidates_[transport_name].end(),
297 candidates.begin(), candidates.end());
298 ++candidates_signal_count_;
299 }
300
Zhi Huang365381f2018-04-13 16:44:34 -0700301 // JsepTransportController::Observer overrides.
Piotr (Peter) Slatalacc8e8bb2018-11-15 08:26:19 -0800302 bool OnTransportChanged(const std::string& mid,
303 RtpTransportInternal* rtp_transport,
304 cricket::DtlsTransportInternal* dtls_transport,
305 MediaTransportInterface* media_transport) override {
Taylor Brandstettercbaa2542018-04-16 16:42:14 -0700306 changed_rtp_transport_by_mid_[mid] = rtp_transport;
Zhi Huange818b6e2018-02-22 15:26:27 -0800307 changed_dtls_transport_by_mid_[mid] = dtls_transport;
Piotr (Peter) Slatalacc8e8bb2018-11-15 08:26:19 -0800308 changed_media_transport_by_mid_[mid] = media_transport;
Taylor Brandstettercbaa2542018-04-16 16:42:14 -0700309 return true;
Zhi Huange818b6e2018-02-22 15:26:27 -0800310 }
311
312 // Information received from signals from transport controller.
313 cricket::IceConnectionState connection_state_ =
314 cricket::kIceConnectionConnecting;
Jonas Olsson635474e2018-10-18 15:58:17 +0200315 PeerConnectionInterface::IceConnectionState ice_connection_state_ =
316 PeerConnectionInterface::kIceConnectionNew;
317 PeerConnectionInterface::PeerConnectionState combined_connection_state_ =
318 PeerConnectionInterface::PeerConnectionState::kNew;
Zhi Huange818b6e2018-02-22 15:26:27 -0800319 bool receiving_ = false;
320 cricket::IceGatheringState gathering_state_ = cricket::kIceGatheringNew;
321 // transport_name => candidates
322 std::map<std::string, Candidates> candidates_;
323 // Counts of each signal emitted.
324 int connection_state_signal_count_ = 0;
Jonas Olsson635474e2018-10-18 15:58:17 +0200325 int ice_connection_state_signal_count_ = 0;
326 int combined_connection_state_signal_count_ = 0;
Zhi Huange818b6e2018-02-22 15:26:27 -0800327 int receiving_signal_count_ = 0;
328 int gathering_state_signal_count_ = 0;
329 int candidates_signal_count_ = 0;
330
331 // |network_thread_| should be destroyed after |transport_controller_|
332 std::unique_ptr<rtc::Thread> network_thread_;
Zhi Huange818b6e2018-02-22 15:26:27 -0800333 std::unique_ptr<FakeTransportFactory> fake_transport_factory_;
334 rtc::Thread* const signaling_thread_ = nullptr;
335 bool signaled_on_non_signaling_thread_ = false;
336 // Used to verify the SignalRtpTransportChanged/SignalDtlsTransportChanged are
337 // signaled correctly.
338 std::map<std::string, RtpTransportInternal*> changed_rtp_transport_by_mid_;
339 std::map<std::string, cricket::DtlsTransportInternal*>
340 changed_dtls_transport_by_mid_;
Piotr (Peter) Slatalacc8e8bb2018-11-15 08:26:19 -0800341 std::map<std::string, MediaTransportInterface*>
342 changed_media_transport_by_mid_;
343
344 // Transport controller needs to be destroyed first, because it may issue
345 // callbacks that modify the changed_*_by_mid in the destructor.
346 std::unique_ptr<JsepTransportController> transport_controller_;
Zhi Huange818b6e2018-02-22 15:26:27 -0800347};
348
349TEST_F(JsepTransportControllerTest, GetRtpTransport) {
350 CreateJsepTransportController(JsepTransportController::Config());
351 auto description = CreateSessionDescriptionWithoutBundle();
352 EXPECT_TRUE(transport_controller_
353 ->SetLocalDescription(SdpType::kOffer, description.get())
354 .ok());
355 auto audio_rtp_transport = transport_controller_->GetRtpTransport(kAudioMid1);
356 auto video_rtp_transport = transport_controller_->GetRtpTransport(kVideoMid1);
357 EXPECT_NE(nullptr, audio_rtp_transport);
358 EXPECT_NE(nullptr, video_rtp_transport);
359 EXPECT_NE(audio_rtp_transport, video_rtp_transport);
360 // Return nullptr for non-existing ones.
361 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kAudioMid2));
362}
363
364TEST_F(JsepTransportControllerTest, GetDtlsTransport) {
365 JsepTransportController::Config config;
366 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
367 CreateJsepTransportController(config);
368 auto description = CreateSessionDescriptionWithoutBundle();
369 EXPECT_TRUE(transport_controller_
370 ->SetLocalDescription(SdpType::kOffer, description.get())
371 .ok());
372 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kAudioMid1));
373 EXPECT_NE(nullptr, transport_controller_->GetRtcpDtlsTransport(kAudioMid1));
374 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kVideoMid1));
375 EXPECT_NE(nullptr, transport_controller_->GetRtcpDtlsTransport(kVideoMid1));
376 // Return nullptr for non-existing ones.
377 EXPECT_EQ(nullptr, transport_controller_->GetDtlsTransport(kVideoMid2));
378 EXPECT_EQ(nullptr, transport_controller_->GetRtcpDtlsTransport(kVideoMid2));
379}
380
Zhi Huange830e682018-03-30 10:48:35 -0700381TEST_F(JsepTransportControllerTest, GetDtlsTransportWithRtcpMux) {
382 JsepTransportController::Config config;
383 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
384 CreateJsepTransportController(config);
385 auto description = CreateSessionDescriptionWithoutBundle();
386 EXPECT_TRUE(transport_controller_
387 ->SetLocalDescription(SdpType::kOffer, description.get())
388 .ok());
389 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kAudioMid1));
390 EXPECT_EQ(nullptr, transport_controller_->GetRtcpDtlsTransport(kAudioMid1));
391 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kVideoMid1));
392 EXPECT_EQ(nullptr, transport_controller_->GetRtcpDtlsTransport(kVideoMid1));
Anton Sukhanov7940da02018-10-10 10:34:49 -0700393 EXPECT_EQ(nullptr, transport_controller_->GetMediaTransport(kAudioMid1));
394}
395
396TEST_F(JsepTransportControllerTest, GetMediaTransportInCaller) {
397 FakeMediaTransportFactory fake_media_transport_factory;
398 JsepTransportController::Config config;
399
400 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
401 config.media_transport_factory = &fake_media_transport_factory;
402 CreateJsepTransportController(config);
403 auto description = CreateSessionDescriptionWithoutBundle();
Piotr (Peter) Slatala9f956252018-10-31 08:25:26 -0700404 AddCryptoSettings(description.get());
405
Anton Sukhanov7940da02018-10-10 10:34:49 -0700406 EXPECT_TRUE(transport_controller_
407 ->SetLocalDescription(SdpType::kOffer, description.get())
408 .ok());
409
410 FakeMediaTransport* media_transport = static_cast<FakeMediaTransport*>(
411 transport_controller_->GetMediaTransport(kAudioMid1));
412
413 ASSERT_NE(nullptr, media_transport);
414
415 // After SetLocalDescription, media transport should be created as caller.
416 EXPECT_TRUE(media_transport->is_caller());
Piotr (Peter) Slatala9f956252018-10-31 08:25:26 -0700417 EXPECT_TRUE(media_transport->pre_shared_key().has_value());
Anton Sukhanov7940da02018-10-10 10:34:49 -0700418
419 // Return nullptr for non-existing mids.
420 EXPECT_EQ(nullptr, transport_controller_->GetMediaTransport(kVideoMid2));
421}
422
423TEST_F(JsepTransportControllerTest, GetMediaTransportInCallee) {
424 FakeMediaTransportFactory fake_media_transport_factory;
425 JsepTransportController::Config config;
426
427 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
428 config.media_transport_factory = &fake_media_transport_factory;
429 CreateJsepTransportController(config);
430 auto description = CreateSessionDescriptionWithoutBundle();
Piotr (Peter) Slatala9f956252018-10-31 08:25:26 -0700431 AddCryptoSettings(description.get());
Anton Sukhanov7940da02018-10-10 10:34:49 -0700432 EXPECT_TRUE(transport_controller_
433 ->SetRemoteDescription(SdpType::kOffer, description.get())
434 .ok());
435
436 FakeMediaTransport* media_transport = static_cast<FakeMediaTransport*>(
437 transport_controller_->GetMediaTransport(kAudioMid1));
438
439 ASSERT_NE(nullptr, media_transport);
440
441 // After SetRemoteDescription, media transport should be created as callee.
442 EXPECT_FALSE(media_transport->is_caller());
Piotr (Peter) Slatala9f956252018-10-31 08:25:26 -0700443 EXPECT_TRUE(media_transport->pre_shared_key().has_value());
Anton Sukhanov7940da02018-10-10 10:34:49 -0700444
445 // Return nullptr for non-existing mids.
446 EXPECT_EQ(nullptr, transport_controller_->GetMediaTransport(kVideoMid2));
Zhi Huange830e682018-03-30 10:48:35 -0700447}
448
Piotr (Peter) Slatala9f956252018-10-31 08:25:26 -0700449TEST_F(JsepTransportControllerTest, GetMediaTransportIsNotSetIfNoSdes) {
450 FakeMediaTransportFactory fake_media_transport_factory;
451 JsepTransportController::Config config;
452
453 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
454 config.media_transport_factory = &fake_media_transport_factory;
455 CreateJsepTransportController(config);
456 auto description = CreateSessionDescriptionWithoutBundle();
457 EXPECT_TRUE(transport_controller_
458 ->SetRemoteDescription(SdpType::kOffer, description.get())
459 .ok());
460
461 EXPECT_EQ(nullptr, transport_controller_->GetMediaTransport(kAudioMid1));
462
463 // Even if we set local description with crypto now (after the remote offer
464 // was set), media transport won't be provided.
465 auto description2 = CreateSessionDescriptionWithoutBundle();
466 AddCryptoSettings(description2.get());
467 EXPECT_TRUE(transport_controller_
468 ->SetLocalDescription(SdpType::kAnswer, description2.get())
469 .ok());
470
471 EXPECT_EQ(nullptr, transport_controller_->GetMediaTransport(kAudioMid1));
472}
473
474TEST_F(JsepTransportControllerTest,
475 AfterSettingAnswerTheSameMediaTransportIsReturned) {
476 FakeMediaTransportFactory fake_media_transport_factory;
477 JsepTransportController::Config config;
478
479 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
480 config.media_transport_factory = &fake_media_transport_factory;
481 CreateJsepTransportController(config);
482 auto description = CreateSessionDescriptionWithoutBundle();
483 AddCryptoSettings(description.get());
484 EXPECT_TRUE(transport_controller_
485 ->SetRemoteDescription(SdpType::kOffer, description.get())
486 .ok());
487
488 FakeMediaTransport* media_transport = static_cast<FakeMediaTransport*>(
489 transport_controller_->GetMediaTransport(kAudioMid1));
490 EXPECT_NE(nullptr, media_transport);
491 EXPECT_TRUE(media_transport->pre_shared_key().has_value());
492
493 // Even if we set local description with crypto now (after the remote offer
494 // was set), media transport won't be provided.
495 auto description2 = CreateSessionDescriptionWithoutBundle();
496 AddCryptoSettings(description2.get());
497
498 RTCError result = transport_controller_->SetLocalDescription(
499 SdpType::kAnswer, description2.get());
500 EXPECT_TRUE(result.ok()) << result.message();
501
502 // Media transport did not change.
503 EXPECT_EQ(media_transport,
504 transport_controller_->GetMediaTransport(kAudioMid1));
505}
506
Zhi Huange818b6e2018-02-22 15:26:27 -0800507TEST_F(JsepTransportControllerTest, SetIceConfig) {
508 CreateJsepTransportController(JsepTransportController::Config());
509 auto description = CreateSessionDescriptionWithoutBundle();
510 EXPECT_TRUE(transport_controller_
511 ->SetLocalDescription(SdpType::kOffer, description.get())
512 .ok());
513
514 transport_controller_->SetIceConfig(
515 CreateIceConfig(kTimeout, cricket::GATHER_CONTINUALLY));
516 FakeDtlsTransport* fake_audio_dtls = static_cast<FakeDtlsTransport*>(
517 transport_controller_->GetDtlsTransport(kAudioMid1));
518 ASSERT_NE(nullptr, fake_audio_dtls);
519 EXPECT_EQ(kTimeout,
520 fake_audio_dtls->fake_ice_transport()->receiving_timeout());
521 EXPECT_TRUE(fake_audio_dtls->fake_ice_transport()->gather_continually());
522
523 // Test that value stored in controller is applied to new transports.
524 AddAudioSection(description.get(), kAudioMid2, kIceUfrag1, kIcePwd1,
525 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
526 nullptr);
527
528 EXPECT_TRUE(transport_controller_
529 ->SetLocalDescription(SdpType::kOffer, description.get())
530 .ok());
531 fake_audio_dtls = static_cast<FakeDtlsTransport*>(
532 transport_controller_->GetDtlsTransport(kAudioMid2));
533 ASSERT_NE(nullptr, fake_audio_dtls);
534 EXPECT_EQ(kTimeout,
535 fake_audio_dtls->fake_ice_transport()->receiving_timeout());
536 EXPECT_TRUE(fake_audio_dtls->fake_ice_transport()->gather_continually());
537}
538
539// Tests the getter and setter of the ICE restart flag.
540TEST_F(JsepTransportControllerTest, NeedIceRestart) {
541 CreateJsepTransportController(JsepTransportController::Config());
542 auto description = CreateSessionDescriptionWithoutBundle();
543 EXPECT_TRUE(transport_controller_
544 ->SetLocalDescription(SdpType::kOffer, description.get())
545 .ok());
546 EXPECT_TRUE(transport_controller_
547 ->SetRemoteDescription(SdpType::kAnswer, description.get())
548 .ok());
549
550 // Initially NeedsIceRestart should return false.
551 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kAudioMid1));
552 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kVideoMid1));
553 // Set the needs-ice-restart flag and verify NeedsIceRestart starts returning
554 // true.
555 transport_controller_->SetNeedsIceRestartFlag();
556 EXPECT_TRUE(transport_controller_->NeedsIceRestart(kAudioMid1));
557 EXPECT_TRUE(transport_controller_->NeedsIceRestart(kVideoMid1));
558 // For a nonexistent transport, false should be returned.
559 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kVideoMid2));
560
561 // Reset the ice_ufrag/ice_pwd for audio.
562 auto audio_transport_info = description->GetTransportInfoByName(kAudioMid1);
563 audio_transport_info->description.ice_ufrag = kIceUfrag2;
564 audio_transport_info->description.ice_pwd = kIcePwd2;
565 EXPECT_TRUE(transport_controller_
566 ->SetLocalDescription(SdpType::kOffer, description.get())
567 .ok());
568 // Because the ICE is only restarted for audio, NeedsIceRestart is expected to
569 // return false for audio and true for video.
570 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kAudioMid1));
571 EXPECT_TRUE(transport_controller_->NeedsIceRestart(kVideoMid1));
572}
573
574TEST_F(JsepTransportControllerTest, MaybeStartGathering) {
575 CreateJsepTransportController(JsepTransportController::Config());
576 auto description = CreateSessionDescriptionWithBundleGroup();
577 EXPECT_TRUE(transport_controller_
578 ->SetLocalDescription(SdpType::kOffer, description.get())
579 .ok());
580 // After setting the local description, we should be able to start gathering
581 // candidates.
582 transport_controller_->MaybeStartGathering();
583 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
584 EXPECT_EQ(1, gathering_state_signal_count_);
585}
586
587TEST_F(JsepTransportControllerTest, AddRemoveRemoteCandidates) {
588 CreateJsepTransportController(JsepTransportController::Config());
589 auto description = CreateSessionDescriptionWithoutBundle();
590 transport_controller_->SetLocalDescription(SdpType::kOffer,
591 description.get());
592 transport_controller_->SetRemoteDescription(SdpType::kAnswer,
593 description.get());
594 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
595 transport_controller_->GetDtlsTransport(kAudioMid1));
596 ASSERT_NE(nullptr, fake_audio_dtls);
597 Candidates candidates;
598 candidates.push_back(
599 CreateCandidate(kAudioMid1, cricket::ICE_CANDIDATE_COMPONENT_RTP));
600 EXPECT_TRUE(
601 transport_controller_->AddRemoteCandidates(kAudioMid1, candidates).ok());
602 EXPECT_EQ(1U,
603 fake_audio_dtls->fake_ice_transport()->remote_candidates().size());
604
605 EXPECT_TRUE(transport_controller_->RemoveRemoteCandidates(candidates).ok());
606 EXPECT_EQ(0U,
607 fake_audio_dtls->fake_ice_transport()->remote_candidates().size());
608}
609
610TEST_F(JsepTransportControllerTest, SetAndGetLocalCertificate) {
611 CreateJsepTransportController(JsepTransportController::Config());
612
613 rtc::scoped_refptr<rtc::RTCCertificate> certificate1 =
614 rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
615 rtc::SSLIdentity::Generate("session1", rtc::KT_DEFAULT)));
616 rtc::scoped_refptr<rtc::RTCCertificate> returned_certificate;
617
Karl Wiberg918f50c2018-07-05 11:40:33 +0200618 auto description = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800619 AddAudioSection(description.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
620 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
621 certificate1);
622
623 // Apply the local certificate.
624 EXPECT_TRUE(transport_controller_->SetLocalCertificate(certificate1));
625 // Apply the local description.
626 EXPECT_TRUE(transport_controller_
627 ->SetLocalDescription(SdpType::kOffer, description.get())
628 .ok());
629 returned_certificate = transport_controller_->GetLocalCertificate(kAudioMid1);
630 EXPECT_TRUE(returned_certificate);
631 EXPECT_EQ(certificate1->identity()->certificate().ToPEMString(),
632 returned_certificate->identity()->certificate().ToPEMString());
633
634 // Should fail if called for a nonexistant transport.
635 EXPECT_EQ(nullptr, transport_controller_->GetLocalCertificate(kVideoMid1));
636
637 // Shouldn't be able to change the identity once set.
638 rtc::scoped_refptr<rtc::RTCCertificate> certificate2 =
639 rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
640 rtc::SSLIdentity::Generate("session2", rtc::KT_DEFAULT)));
641 EXPECT_FALSE(transport_controller_->SetLocalCertificate(certificate2));
642}
643
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800644TEST_F(JsepTransportControllerTest, GetRemoteSSLCertChain) {
Zhi Huange818b6e2018-02-22 15:26:27 -0800645 CreateJsepTransportController(JsepTransportController::Config());
646 auto description = CreateSessionDescriptionWithBundleGroup();
647 EXPECT_TRUE(transport_controller_
648 ->SetLocalDescription(SdpType::kOffer, description.get())
649 .ok());
650 rtc::FakeSSLCertificate fake_certificate("fake_data");
651
652 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
653 transport_controller_->GetDtlsTransport(kAudioMid1));
654 fake_audio_dtls->SetRemoteSSLCertificate(&fake_certificate);
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800655 std::unique_ptr<rtc::SSLCertChain> returned_cert_chain =
656 transport_controller_->GetRemoteSSLCertChain(kAudioMid1);
657 ASSERT_TRUE(returned_cert_chain);
658 ASSERT_EQ(1u, returned_cert_chain->GetSize());
Zhi Huange818b6e2018-02-22 15:26:27 -0800659 EXPECT_EQ(fake_certificate.ToPEMString(),
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800660 returned_cert_chain->Get(0).ToPEMString());
Zhi Huange818b6e2018-02-22 15:26:27 -0800661
662 // Should fail if called for a nonexistant transport.
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800663 EXPECT_FALSE(transport_controller_->GetRemoteSSLCertChain(kAudioMid2));
Zhi Huange818b6e2018-02-22 15:26:27 -0800664}
665
666TEST_F(JsepTransportControllerTest, GetDtlsRole) {
667 CreateJsepTransportController(JsepTransportController::Config());
668 auto offer_certificate =
669 rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
670 rtc::SSLIdentity::Generate("offer", rtc::KT_DEFAULT)));
671 auto answer_certificate =
672 rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
673 rtc::SSLIdentity::Generate("answer", rtc::KT_DEFAULT)));
674 transport_controller_->SetLocalCertificate(offer_certificate);
675
Karl Wiberg918f50c2018-07-05 11:40:33 +0200676 auto offer_desc = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800677 AddAudioSection(offer_desc.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
678 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
679 offer_certificate);
Karl Wiberg918f50c2018-07-05 11:40:33 +0200680 auto answer_desc = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800681 AddAudioSection(answer_desc.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
682 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
683 answer_certificate);
684
685 EXPECT_TRUE(transport_controller_
686 ->SetLocalDescription(SdpType::kOffer, offer_desc.get())
687 .ok());
688
Danil Chapovalov66cadcc2018-06-19 16:47:43 +0200689 absl::optional<rtc::SSLRole> role =
Zhi Huange818b6e2018-02-22 15:26:27 -0800690 transport_controller_->GetDtlsRole(kAudioMid1);
691 // The DTLS role is not decided yet.
692 EXPECT_FALSE(role);
693 EXPECT_TRUE(transport_controller_
694 ->SetRemoteDescription(SdpType::kAnswer, answer_desc.get())
695 .ok());
696 role = transport_controller_->GetDtlsRole(kAudioMid1);
697
698 ASSERT_TRUE(role);
699 EXPECT_EQ(rtc::SSL_CLIENT, *role);
700}
701
702TEST_F(JsepTransportControllerTest, GetStats) {
703 CreateJsepTransportController(JsepTransportController::Config());
704 auto description = CreateSessionDescriptionWithBundleGroup();
705 EXPECT_TRUE(transport_controller_
706 ->SetLocalDescription(SdpType::kOffer, description.get())
707 .ok());
708
709 cricket::TransportStats stats;
710 EXPECT_TRUE(transport_controller_->GetStats(kAudioMid1, &stats));
711 EXPECT_EQ(kAudioMid1, stats.transport_name);
712 EXPECT_EQ(1u, stats.channel_stats.size());
713 // Return false for non-existing transport.
714 EXPECT_FALSE(transport_controller_->GetStats(kAudioMid2, &stats));
715}
716
717TEST_F(JsepTransportControllerTest, SignalConnectionStateFailed) {
718 CreateJsepTransportController(JsepTransportController::Config());
719 auto description = CreateSessionDescriptionWithoutBundle();
720 EXPECT_TRUE(transport_controller_
721 ->SetLocalDescription(SdpType::kOffer, description.get())
722 .ok());
723
724 auto fake_ice = static_cast<cricket::FakeIceTransport*>(
725 transport_controller_->GetDtlsTransport(kAudioMid1)->ice_transport());
726 fake_ice->SetCandidatesGatheringComplete();
727 fake_ice->SetConnectionCount(1);
728 // The connection stats will be failed if there is no active connection.
729 fake_ice->SetConnectionCount(0);
730 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
731 EXPECT_EQ(1, connection_state_signal_count_);
Jonas Olssond8aa9f92018-11-09 10:51:09 +0100732 EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionFailed,
733 ice_connection_state_, kTimeout);
Jonas Olsson635474e2018-10-18 15:58:17 +0200734 EXPECT_EQ(1, ice_connection_state_signal_count_);
Jonas Olssond8aa9f92018-11-09 10:51:09 +0100735 EXPECT_EQ_WAIT(PeerConnectionInterface::PeerConnectionState::kFailed,
736 combined_connection_state_, kTimeout);
Jonas Olsson635474e2018-10-18 15:58:17 +0200737 EXPECT_EQ(1, combined_connection_state_signal_count_);
Zhi Huange818b6e2018-02-22 15:26:27 -0800738}
739
Piotr (Peter) Slatala4eb41122018-11-01 07:26:03 -0700740TEST_F(JsepTransportControllerTest,
741 SignalConnectionStateConnectedNoMediaTransport) {
Zhi Huange818b6e2018-02-22 15:26:27 -0800742 CreateJsepTransportController(JsepTransportController::Config());
743 auto description = CreateSessionDescriptionWithoutBundle();
744 EXPECT_TRUE(transport_controller_
745 ->SetLocalDescription(SdpType::kOffer, description.get())
746 .ok());
747
748 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
749 transport_controller_->GetDtlsTransport(kAudioMid1));
750 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
751 transport_controller_->GetDtlsTransport(kVideoMid1));
752
753 // First, have one transport connect, and another fail, to ensure that
754 // the first transport connecting didn't trigger a "connected" state signal.
755 // We should only get a signal when all are connected.
756 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
757 fake_audio_dtls->SetWritable(true);
758 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
759 // Decrease the number of the connection to trigger the signal.
760 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
761 fake_video_dtls->fake_ice_transport()->SetConnectionCount(0);
762 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
763
764 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
765 EXPECT_EQ(1, connection_state_signal_count_);
Jonas Olssond8aa9f92018-11-09 10:51:09 +0100766 EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionFailed,
767 ice_connection_state_, kTimeout);
Jonas Olsson635474e2018-10-18 15:58:17 +0200768 EXPECT_EQ(1, ice_connection_state_signal_count_);
Jonas Olssond8aa9f92018-11-09 10:51:09 +0100769 EXPECT_EQ_WAIT(PeerConnectionInterface::PeerConnectionState::kFailed,
770 combined_connection_state_, kTimeout);
Jonas Olsson635474e2018-10-18 15:58:17 +0200771 EXPECT_EQ(1, combined_connection_state_signal_count_);
Zhi Huange818b6e2018-02-22 15:26:27 -0800772
Jonas Olsson635474e2018-10-18 15:58:17 +0200773 fake_audio_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
774 fake_video_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
Zhi Huange818b6e2018-02-22 15:26:27 -0800775 // Set the connection count to be 2 and the cricket::FakeIceTransport will set
776 // the transport state to be STATE_CONNECTING.
777 fake_video_dtls->fake_ice_transport()->SetConnectionCount(2);
778 fake_video_dtls->SetWritable(true);
779 EXPECT_EQ_WAIT(cricket::kIceConnectionConnected, connection_state_, kTimeout);
780 EXPECT_EQ(2, connection_state_signal_count_);
Jonas Olssond8aa9f92018-11-09 10:51:09 +0100781 EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionConnected,
782 ice_connection_state_, kTimeout);
Jonas Olsson635474e2018-10-18 15:58:17 +0200783 EXPECT_EQ(2, ice_connection_state_signal_count_);
Jonas Olssond8aa9f92018-11-09 10:51:09 +0100784 EXPECT_EQ_WAIT(PeerConnectionInterface::PeerConnectionState::kConnected,
785 combined_connection_state_, kTimeout);
Jonas Olsson635474e2018-10-18 15:58:17 +0200786 EXPECT_EQ(2, combined_connection_state_signal_count_);
Zhi Huange818b6e2018-02-22 15:26:27 -0800787}
788
Piotr (Peter) Slatala4eb41122018-11-01 07:26:03 -0700789TEST_F(JsepTransportControllerTest,
790 SignalConnectionStateConnectedWithMediaTransport) {
791 FakeMediaTransportFactory fake_media_transport_factory;
792 JsepTransportController::Config config;
793 config.media_transport_factory = &fake_media_transport_factory;
794 CreateJsepTransportController(config);
795 auto description = CreateSessionDescriptionWithoutBundle();
796 AddCryptoSettings(description.get());
797 EXPECT_TRUE(transport_controller_
798 ->SetLocalDescription(SdpType::kOffer, description.get())
799 .ok());
800
801 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
802 transport_controller_->GetDtlsTransport(kAudioMid1));
803 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
804 transport_controller_->GetDtlsTransport(kVideoMid1));
805 fake_audio_dtls->SetWritable(true);
806 fake_video_dtls->SetWritable(true);
807 // Decreasing connection count from 2 to 1 triggers connection state event.
808 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(2);
809 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
810 fake_video_dtls->fake_ice_transport()->SetConnectionCount(2);
811 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
812 fake_audio_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
813 fake_video_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
814
815 // Still not connected, because we are waiting for media transport.
816 EXPECT_EQ_WAIT(cricket::kIceConnectionConnecting, connection_state_,
817 kTimeout);
818
819 FakeMediaTransport* media_transport = static_cast<FakeMediaTransport*>(
820 transport_controller_->GetMediaTransport(kAudioMid1));
821
822 media_transport->SetState(webrtc::MediaTransportState::kWritable);
823 EXPECT_EQ_WAIT(cricket::kIceConnectionConnecting, connection_state_,
824 kTimeout);
825
826 // Still waiting for the second media transport.
827 media_transport = static_cast<FakeMediaTransport*>(
828 transport_controller_->GetMediaTransport(kVideoMid1));
829 media_transport->SetState(webrtc::MediaTransportState::kWritable);
830
831 EXPECT_EQ_WAIT(cricket::kIceConnectionConnected, connection_state_, kTimeout);
832}
833
834TEST_F(JsepTransportControllerTest,
835 SignalConnectionStateFailedWhenMediaTransportClosed) {
836 FakeMediaTransportFactory fake_media_transport_factory;
837 JsepTransportController::Config config;
838 config.media_transport_factory = &fake_media_transport_factory;
839 CreateJsepTransportController(config);
840 auto description = CreateSessionDescriptionWithoutBundle();
841 AddCryptoSettings(description.get());
842 EXPECT_TRUE(transport_controller_
843 ->SetLocalDescription(SdpType::kOffer, description.get())
844 .ok());
845
846 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
847 transport_controller_->GetDtlsTransport(kAudioMid1));
848 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
849 transport_controller_->GetDtlsTransport(kVideoMid1));
850 fake_audio_dtls->SetWritable(true);
851 fake_video_dtls->SetWritable(true);
852 // Decreasing connection count from 2 to 1 triggers connection state event.
853 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(2);
854 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
855 fake_video_dtls->fake_ice_transport()->SetConnectionCount(2);
856 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
857 fake_audio_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
858 fake_video_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
859
860 FakeMediaTransport* media_transport = static_cast<FakeMediaTransport*>(
861 transport_controller_->GetMediaTransport(kAudioMid1));
862
863 media_transport->SetState(webrtc::MediaTransportState::kWritable);
864
865 media_transport = static_cast<FakeMediaTransport*>(
866 transport_controller_->GetMediaTransport(kVideoMid1));
867
868 media_transport->SetState(webrtc::MediaTransportState::kWritable);
869
870 EXPECT_EQ_WAIT(cricket::kIceConnectionConnected, connection_state_, kTimeout);
871
872 media_transport->SetState(webrtc::MediaTransportState::kClosed);
873 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
874}
875
Zhi Huange818b6e2018-02-22 15:26:27 -0800876TEST_F(JsepTransportControllerTest, SignalConnectionStateComplete) {
877 CreateJsepTransportController(JsepTransportController::Config());
878 auto description = CreateSessionDescriptionWithoutBundle();
879 EXPECT_TRUE(transport_controller_
880 ->SetLocalDescription(SdpType::kOffer, description.get())
881 .ok());
882
883 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
884 transport_controller_->GetDtlsTransport(kAudioMid1));
885 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
886 transport_controller_->GetDtlsTransport(kVideoMid1));
887
888 // First, have one transport connect, and another fail, to ensure that
889 // the first transport connecting didn't trigger a "connected" state signal.
890 // We should only get a signal when all are connected.
891 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
892 fake_audio_dtls->SetWritable(true);
893 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
894 // Decrease the number of the connection to trigger the signal.
895 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
896 fake_video_dtls->fake_ice_transport()->SetConnectionCount(0);
897 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
898
899 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
900 EXPECT_EQ(1, connection_state_signal_count_);
Jonas Olssond8aa9f92018-11-09 10:51:09 +0100901 EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionFailed,
902 ice_connection_state_, kTimeout);
Jonas Olsson635474e2018-10-18 15:58:17 +0200903 EXPECT_EQ(1, ice_connection_state_signal_count_);
Jonas Olssond8aa9f92018-11-09 10:51:09 +0100904 EXPECT_EQ_WAIT(PeerConnectionInterface::PeerConnectionState::kFailed,
905 combined_connection_state_, kTimeout);
Jonas Olsson635474e2018-10-18 15:58:17 +0200906 EXPECT_EQ(1, combined_connection_state_signal_count_);
Zhi Huange818b6e2018-02-22 15:26:27 -0800907
Jonas Olsson635474e2018-10-18 15:58:17 +0200908 fake_audio_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
909 fake_video_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
Zhi Huange818b6e2018-02-22 15:26:27 -0800910 // Set the connection count to be 1 and the cricket::FakeIceTransport will set
911 // the transport state to be STATE_COMPLETED.
912 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
913 fake_video_dtls->SetWritable(true);
914 EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
915 EXPECT_EQ(2, connection_state_signal_count_);
Jonas Olssond8aa9f92018-11-09 10:51:09 +0100916 EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionCompleted,
917 ice_connection_state_, kTimeout);
Jonas Olsson635474e2018-10-18 15:58:17 +0200918 EXPECT_EQ(2, ice_connection_state_signal_count_);
Jonas Olssond8aa9f92018-11-09 10:51:09 +0100919 EXPECT_EQ_WAIT(PeerConnectionInterface::PeerConnectionState::kConnected,
920 combined_connection_state_, kTimeout);
Jonas Olsson635474e2018-10-18 15:58:17 +0200921 EXPECT_EQ(2, combined_connection_state_signal_count_);
Zhi Huange818b6e2018-02-22 15:26:27 -0800922}
923
924TEST_F(JsepTransportControllerTest, SignalIceGatheringStateGathering) {
925 CreateJsepTransportController(JsepTransportController::Config());
926 auto description = CreateSessionDescriptionWithoutBundle();
927 EXPECT_TRUE(transport_controller_
928 ->SetLocalDescription(SdpType::kOffer, description.get())
929 .ok());
930
931 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
932 transport_controller_->GetDtlsTransport(kAudioMid1));
933 fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
934 // Should be in the gathering state as soon as any transport starts gathering.
935 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
936 EXPECT_EQ(1, gathering_state_signal_count_);
937}
938
939TEST_F(JsepTransportControllerTest, SignalIceGatheringStateComplete) {
940 CreateJsepTransportController(JsepTransportController::Config());
941 auto description = CreateSessionDescriptionWithoutBundle();
942 EXPECT_TRUE(transport_controller_
943 ->SetLocalDescription(SdpType::kOffer, description.get())
944 .ok());
945
946 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
947 transport_controller_->GetDtlsTransport(kAudioMid1));
948 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
949 transport_controller_->GetDtlsTransport(kVideoMid1));
950
951 fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
952 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
953 EXPECT_EQ(1, gathering_state_signal_count_);
954
955 // Have one transport finish gathering, to make sure gathering
956 // completion wasn't signalled if only one transport finished gathering.
957 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
958 EXPECT_EQ(1, gathering_state_signal_count_);
959
960 fake_video_dtls->fake_ice_transport()->MaybeStartGathering();
961 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
962 EXPECT_EQ(1, gathering_state_signal_count_);
963
964 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
965 EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
966 EXPECT_EQ(2, gathering_state_signal_count_);
967}
968
969// Test that when the last transport that hasn't finished connecting and/or
970// gathering is destroyed, the aggregate state jumps to "completed". This can
971// happen if, for example, we have an audio and video transport, the audio
972// transport completes, then we start bundling video on the audio transport.
973TEST_F(JsepTransportControllerTest,
974 SignalingWhenLastIncompleteTransportDestroyed) {
975 CreateJsepTransportController(JsepTransportController::Config());
976 auto description = CreateSessionDescriptionWithBundleGroup();
977 EXPECT_TRUE(transport_controller_
978 ->SetLocalDescription(SdpType::kOffer, description.get())
979 .ok());
980
981 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
982 transport_controller_->GetDtlsTransport(kAudioMid1));
983 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
984 transport_controller_->GetDtlsTransport(kVideoMid1));
985 EXPECT_NE(fake_audio_dtls, fake_video_dtls);
986
987 fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
988 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
989 EXPECT_EQ(1, gathering_state_signal_count_);
990
991 // Let the audio transport complete.
992 fake_audio_dtls->SetWritable(true);
993 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
994 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
Jonas Olsson635474e2018-10-18 15:58:17 +0200995 fake_audio_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
Zhi Huange818b6e2018-02-22 15:26:27 -0800996 EXPECT_EQ(1, gathering_state_signal_count_);
997
998 // Set the remote description and enable the bundle.
999 EXPECT_TRUE(transport_controller_
1000 ->SetRemoteDescription(SdpType::kAnswer, description.get())
1001 .ok());
1002 // The BUNDLE should be enabled, the incomplete video transport should be
1003 // deleted and the states shoud be updated.
1004 fake_video_dtls = static_cast<FakeDtlsTransport*>(
1005 transport_controller_->GetDtlsTransport(kVideoMid1));
1006 EXPECT_EQ(fake_audio_dtls, fake_video_dtls);
1007 EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
Jonas Olsson635474e2018-10-18 15:58:17 +02001008 EXPECT_EQ(PeerConnectionInterface::kIceConnectionCompleted,
1009 ice_connection_state_);
1010 EXPECT_EQ(PeerConnectionInterface::PeerConnectionState::kConnected,
1011 combined_connection_state_);
Zhi Huange818b6e2018-02-22 15:26:27 -08001012 EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
1013 EXPECT_EQ(2, gathering_state_signal_count_);
1014}
1015
1016TEST_F(JsepTransportControllerTest, SignalCandidatesGathered) {
1017 CreateJsepTransportController(JsepTransportController::Config());
1018 auto description = CreateSessionDescriptionWithBundleGroup();
1019 EXPECT_TRUE(transport_controller_
1020 ->SetLocalDescription(SdpType::kOffer, description.get())
1021 .ok());
1022 transport_controller_->MaybeStartGathering();
1023
1024 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
1025 transport_controller_->GetDtlsTransport(kAudioMid1));
1026 fake_audio_dtls->fake_ice_transport()->SignalCandidateGathered(
1027 fake_audio_dtls->fake_ice_transport(), CreateCandidate(kAudioMid1, 1));
1028 EXPECT_EQ_WAIT(1, candidates_signal_count_, kTimeout);
1029 EXPECT_EQ(1u, candidates_[kAudioMid1].size());
1030}
1031
1032TEST_F(JsepTransportControllerTest, IceSignalingOccursOnSignalingThread) {
1033 network_thread_ = rtc::Thread::CreateWithSocketServer();
1034 network_thread_->Start();
1035 CreateJsepTransportController(JsepTransportController::Config(),
1036 signaling_thread_, network_thread_.get(),
1037 /*PortAllocator=*/nullptr);
1038 CreateLocalDescriptionAndCompleteConnectionOnNetworkThread();
1039
1040 // connecting --> connected --> completed
1041 EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
1042 EXPECT_EQ(2, connection_state_signal_count_);
1043
1044 // new --> gathering --> complete
1045 EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
1046 EXPECT_EQ(2, gathering_state_signal_count_);
1047
1048 EXPECT_EQ_WAIT(1u, candidates_[kAudioMid1].size(), kTimeout);
1049 EXPECT_EQ_WAIT(1u, candidates_[kVideoMid1].size(), kTimeout);
1050 EXPECT_EQ(2, candidates_signal_count_);
1051
1052 EXPECT_TRUE(!signaled_on_non_signaling_thread_);
1053}
1054
1055// Older versions of Chrome expect the ICE role to be re-determined when an
1056// ICE restart occurs, and also don't perform conflict resolution correctly,
1057// so for now we can't safely stop doing this.
1058// See: https://bugs.chromium.org/p/chromium/issues/detail?id=628676
1059// TODO(deadbeef): Remove this when these old versions of Chrome reach a low
1060// enough population.
1061TEST_F(JsepTransportControllerTest, IceRoleRedeterminedOnIceRestartByDefault) {
1062 CreateJsepTransportController(JsepTransportController::Config());
1063 // Let the |transport_controller_| be the controlled side initially.
Karl Wiberg918f50c2018-07-05 11:40:33 +02001064 auto remote_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001065 AddAudioSection(remote_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1066 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1067 nullptr);
Karl Wiberg918f50c2018-07-05 11:40:33 +02001068 auto local_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001069 AddAudioSection(local_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1070 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1071 nullptr);
1072
1073 EXPECT_TRUE(transport_controller_
1074 ->SetRemoteDescription(SdpType::kOffer, remote_offer.get())
1075 .ok());
1076 EXPECT_TRUE(transport_controller_
1077 ->SetLocalDescription(SdpType::kAnswer, local_answer.get())
1078 .ok());
1079
1080 auto fake_dtls = static_cast<FakeDtlsTransport*>(
1081 transport_controller_->GetDtlsTransport(kAudioMid1));
1082 EXPECT_EQ(cricket::ICEROLE_CONTROLLED,
1083 fake_dtls->fake_ice_transport()->GetIceRole());
1084
1085 // New offer will trigger the ICE restart.
Karl Wiberg918f50c2018-07-05 11:40:33 +02001086 auto restart_local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001087 AddAudioSection(restart_local_offer.get(), kAudioMid1, kIceUfrag3, kIcePwd3,
1088 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1089 nullptr);
1090 EXPECT_TRUE(
1091 transport_controller_
1092 ->SetLocalDescription(SdpType::kOffer, restart_local_offer.get())
1093 .ok());
1094 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
1095 fake_dtls->fake_ice_transport()->GetIceRole());
1096}
1097
1098// Test that if the TransportController was created with the
1099// |redetermine_role_on_ice_restart| parameter set to false, the role is *not*
1100// redetermined on an ICE restart.
1101TEST_F(JsepTransportControllerTest, IceRoleNotRedetermined) {
1102 JsepTransportController::Config config;
1103 config.redetermine_role_on_ice_restart = false;
1104
1105 CreateJsepTransportController(config);
1106 // Let the |transport_controller_| be the controlled side initially.
Karl Wiberg918f50c2018-07-05 11:40:33 +02001107 auto remote_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001108 AddAudioSection(remote_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1109 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1110 nullptr);
Karl Wiberg918f50c2018-07-05 11:40:33 +02001111 auto local_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001112 AddAudioSection(local_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1113 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1114 nullptr);
1115
1116 EXPECT_TRUE(transport_controller_
1117 ->SetRemoteDescription(SdpType::kOffer, remote_offer.get())
1118 .ok());
1119 EXPECT_TRUE(transport_controller_
1120 ->SetLocalDescription(SdpType::kAnswer, local_answer.get())
1121 .ok());
1122
1123 auto fake_dtls = static_cast<FakeDtlsTransport*>(
1124 transport_controller_->GetDtlsTransport(kAudioMid1));
1125 EXPECT_EQ(cricket::ICEROLE_CONTROLLED,
1126 fake_dtls->fake_ice_transport()->GetIceRole());
1127
1128 // New offer will trigger the ICE restart.
Karl Wiberg918f50c2018-07-05 11:40:33 +02001129 auto restart_local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001130 AddAudioSection(restart_local_offer.get(), kAudioMid1, kIceUfrag3, kIcePwd3,
1131 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1132 nullptr);
1133 EXPECT_TRUE(
1134 transport_controller_
1135 ->SetLocalDescription(SdpType::kOffer, restart_local_offer.get())
1136 .ok());
1137 EXPECT_EQ(cricket::ICEROLE_CONTROLLED,
1138 fake_dtls->fake_ice_transport()->GetIceRole());
1139}
1140
1141// Tests ICE-Lite mode in remote answer.
1142TEST_F(JsepTransportControllerTest, SetIceRoleWhenIceLiteInRemoteAnswer) {
1143 CreateJsepTransportController(JsepTransportController::Config());
Karl Wiberg918f50c2018-07-05 11:40:33 +02001144 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001145 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1146 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1147 nullptr);
1148 EXPECT_TRUE(transport_controller_
1149 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1150 .ok());
1151 auto fake_dtls = static_cast<FakeDtlsTransport*>(
1152 transport_controller_->GetDtlsTransport(kAudioMid1));
1153 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
1154 fake_dtls->fake_ice_transport()->GetIceRole());
1155 EXPECT_EQ(cricket::ICEMODE_FULL,
1156 fake_dtls->fake_ice_transport()->remote_ice_mode());
1157
Karl Wiberg918f50c2018-07-05 11:40:33 +02001158 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001159 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1160 cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_PASSIVE,
1161 nullptr);
1162 EXPECT_TRUE(transport_controller_
1163 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1164 .ok());
1165 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
1166 fake_dtls->fake_ice_transport()->GetIceRole());
1167 EXPECT_EQ(cricket::ICEMODE_LITE,
1168 fake_dtls->fake_ice_transport()->remote_ice_mode());
1169}
1170
1171// Tests that the ICE role remains "controlling" if a subsequent offer that
1172// does an ICE restart is received from an ICE lite endpoint. Regression test
1173// for: https://crbug.com/710760
1174TEST_F(JsepTransportControllerTest,
1175 IceRoleIsControllingAfterIceRestartFromIceLiteEndpoint) {
1176 CreateJsepTransportController(JsepTransportController::Config());
Karl Wiberg918f50c2018-07-05 11:40:33 +02001177 auto remote_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001178 AddAudioSection(remote_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1179 cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_ACTPASS,
1180 nullptr);
Karl Wiberg918f50c2018-07-05 11:40:33 +02001181 auto local_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001182 AddAudioSection(local_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1183 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1184 nullptr);
1185 // Initial Offer/Answer exchange. If the remote offerer is ICE-Lite, then the
1186 // local side is the controlling.
1187 EXPECT_TRUE(transport_controller_
1188 ->SetRemoteDescription(SdpType::kOffer, remote_offer.get())
1189 .ok());
1190 EXPECT_TRUE(transport_controller_
1191 ->SetLocalDescription(SdpType::kAnswer, local_answer.get())
1192 .ok());
1193 auto fake_dtls = static_cast<FakeDtlsTransport*>(
1194 transport_controller_->GetDtlsTransport(kAudioMid1));
1195 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
1196 fake_dtls->fake_ice_transport()->GetIceRole());
1197
1198 // In the subsequence remote offer triggers an ICE restart.
Karl Wiberg918f50c2018-07-05 11:40:33 +02001199 auto remote_offer2 = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001200 AddAudioSection(remote_offer2.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1201 cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_ACTPASS,
1202 nullptr);
Karl Wiberg918f50c2018-07-05 11:40:33 +02001203 auto local_answer2 = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001204 AddAudioSection(local_answer2.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1205 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1206 nullptr);
1207 EXPECT_TRUE(transport_controller_
1208 ->SetRemoteDescription(SdpType::kOffer, remote_offer2.get())
1209 .ok());
1210 EXPECT_TRUE(transport_controller_
1211 ->SetLocalDescription(SdpType::kAnswer, local_answer2.get())
1212 .ok());
1213 fake_dtls = static_cast<FakeDtlsTransport*>(
1214 transport_controller_->GetDtlsTransport(kAudioMid1));
1215 // The local side is still the controlling role since the remote side is using
1216 // ICE-Lite.
1217 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
1218 fake_dtls->fake_ice_transport()->GetIceRole());
1219}
1220
1221// Tests that the SDP has more than one audio/video m= sections.
1222TEST_F(JsepTransportControllerTest, MultipleMediaSectionsOfSameTypeWithBundle) {
1223 CreateJsepTransportController(JsepTransportController::Config());
1224 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1225 bundle_group.AddContentName(kAudioMid1);
1226 bundle_group.AddContentName(kAudioMid2);
1227 bundle_group.AddContentName(kVideoMid1);
1228 bundle_group.AddContentName(kDataMid1);
1229
Karl Wiberg918f50c2018-07-05 11:40:33 +02001230 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001231 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1232 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1233 nullptr);
1234 AddAudioSection(local_offer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
1235 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1236 nullptr);
1237 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1238 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1239 nullptr);
1240 AddDataSection(local_offer.get(), kDataMid1,
1241 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
1242 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1243 nullptr);
1244
Karl Wiberg918f50c2018-07-05 11:40:33 +02001245 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001246 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1247 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1248 nullptr);
1249 AddAudioSection(remote_answer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
1250 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1251 nullptr);
1252 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1253 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1254 nullptr);
1255 AddDataSection(remote_answer.get(), kDataMid1,
1256 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
1257 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1258 nullptr);
1259
1260 local_offer->AddGroup(bundle_group);
1261 remote_answer->AddGroup(bundle_group);
1262
1263 EXPECT_TRUE(transport_controller_
1264 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1265 .ok());
1266 EXPECT_TRUE(transport_controller_
1267 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1268 .ok());
1269 // Verify that all the sections are bundled on kAudio1.
1270 auto transport1 = transport_controller_->GetRtpTransport(kAudioMid1);
1271 auto transport2 = transport_controller_->GetRtpTransport(kAudioMid2);
1272 auto transport3 = transport_controller_->GetRtpTransport(kVideoMid1);
1273 auto transport4 = transport_controller_->GetRtpTransport(kDataMid1);
1274 EXPECT_EQ(transport1, transport2);
1275 EXPECT_EQ(transport1, transport3);
1276 EXPECT_EQ(transport1, transport4);
1277
1278 // Verify the OnRtpTransport/DtlsTransportChanged signals are fired correctly.
1279 auto it = changed_rtp_transport_by_mid_.find(kAudioMid2);
1280 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1281 EXPECT_EQ(transport1, it->second);
1282 it = changed_rtp_transport_by_mid_.find(kAudioMid2);
1283 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1284 EXPECT_EQ(transport1, it->second);
1285 it = changed_rtp_transport_by_mid_.find(kVideoMid1);
1286 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1287 EXPECT_EQ(transport1, it->second);
1288 // Verify the DtlsTransport for the SCTP data channel is reset correctly.
1289 auto it2 = changed_dtls_transport_by_mid_.find(kDataMid1);
1290 ASSERT_TRUE(it2 != changed_dtls_transport_by_mid_.end());
1291 EXPECT_EQ(transport1->rtp_packet_transport(), it2->second);
1292}
1293
1294// Tests that only a subset of all the m= sections are bundled.
1295TEST_F(JsepTransportControllerTest, BundleSubsetOfMediaSections) {
1296 CreateJsepTransportController(JsepTransportController::Config());
1297 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1298 bundle_group.AddContentName(kAudioMid1);
1299 bundle_group.AddContentName(kVideoMid1);
1300
Karl Wiberg918f50c2018-07-05 11:40:33 +02001301 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001302 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1303 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1304 nullptr);
1305 AddAudioSection(local_offer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
1306 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1307 nullptr);
1308 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1309 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1310 nullptr);
1311
Karl Wiberg918f50c2018-07-05 11:40:33 +02001312 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001313 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1314 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1315 nullptr);
1316 AddAudioSection(remote_answer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
1317 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1318 nullptr);
1319 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1320 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1321 nullptr);
1322
1323 local_offer->AddGroup(bundle_group);
1324 remote_answer->AddGroup(bundle_group);
1325 EXPECT_TRUE(transport_controller_
1326 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1327 .ok());
1328 EXPECT_TRUE(transport_controller_
1329 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1330 .ok());
1331
1332 // Verifiy that only |kAudio1| and |kVideo1| are bundled.
1333 auto transport1 = transport_controller_->GetRtpTransport(kAudioMid1);
1334 auto transport2 = transport_controller_->GetRtpTransport(kAudioMid2);
1335 auto transport3 = transport_controller_->GetRtpTransport(kVideoMid1);
1336 EXPECT_NE(transport1, transport2);
1337 EXPECT_EQ(transport1, transport3);
1338
1339 auto it = changed_rtp_transport_by_mid_.find(kVideoMid1);
1340 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1341 EXPECT_EQ(transport1, it->second);
1342 it = changed_rtp_transport_by_mid_.find(kAudioMid2);
Zhi Huangd2248f82018-04-10 14:41:03 -07001343 EXPECT_TRUE(transport2 == it->second);
Zhi Huange818b6e2018-02-22 15:26:27 -08001344}
1345
1346// Tests that the initial offer/answer only have data section and audio/video
1347// sections are added in the subsequent offer.
1348TEST_F(JsepTransportControllerTest, BundleOnDataSectionInSubsequentOffer) {
1349 CreateJsepTransportController(JsepTransportController::Config());
1350 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1351 bundle_group.AddContentName(kDataMid1);
1352
Karl Wiberg918f50c2018-07-05 11:40:33 +02001353 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001354 AddDataSection(local_offer.get(), kDataMid1,
1355 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
1356 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1357 nullptr);
Karl Wiberg918f50c2018-07-05 11:40:33 +02001358 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001359 AddDataSection(remote_answer.get(), kDataMid1,
1360 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
1361 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1362 nullptr);
1363 local_offer->AddGroup(bundle_group);
1364 remote_answer->AddGroup(bundle_group);
1365
1366 EXPECT_TRUE(transport_controller_
1367 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1368 .ok());
1369 EXPECT_TRUE(transport_controller_
1370 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1371 .ok());
1372 auto data_transport = transport_controller_->GetRtpTransport(kDataMid1);
1373
1374 // Add audio/video sections in subsequent offer.
1375 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1376 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1377 nullptr);
1378 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1379 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1380 nullptr);
1381 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1382 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1383 nullptr);
1384 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1385 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1386 nullptr);
1387
1388 // Reset the bundle group and do another offer/answer exchange.
1389 bundle_group.AddContentName(kAudioMid1);
1390 bundle_group.AddContentName(kVideoMid1);
1391 local_offer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1392 remote_answer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1393 local_offer->AddGroup(bundle_group);
1394 remote_answer->AddGroup(bundle_group);
1395
1396 EXPECT_TRUE(transport_controller_
1397 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1398 .ok());
1399 EXPECT_TRUE(transport_controller_
1400 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1401 .ok());
1402
1403 auto audio_transport = transport_controller_->GetRtpTransport(kAudioMid1);
1404 auto video_transport = transport_controller_->GetRtpTransport(kVideoMid1);
1405 EXPECT_EQ(data_transport, audio_transport);
1406 EXPECT_EQ(data_transport, video_transport);
1407}
1408
1409TEST_F(JsepTransportControllerTest, VideoDataRejectedInAnswer) {
1410 CreateJsepTransportController(JsepTransportController::Config());
1411 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1412 bundle_group.AddContentName(kAudioMid1);
1413 bundle_group.AddContentName(kVideoMid1);
1414 bundle_group.AddContentName(kDataMid1);
1415
Karl Wiberg918f50c2018-07-05 11:40:33 +02001416 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001417 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1418 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1419 nullptr);
1420 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1421 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1422 nullptr);
1423 AddDataSection(local_offer.get(), kDataMid1,
1424 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1425 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1426 nullptr);
1427
Karl Wiberg918f50c2018-07-05 11:40:33 +02001428 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001429 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1430 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1431 nullptr);
1432 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1433 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1434 nullptr);
1435 AddDataSection(remote_answer.get(), kDataMid1,
1436 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1437 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1438 nullptr);
1439 // Reject video and data section.
1440 remote_answer->contents()[1].rejected = true;
1441 remote_answer->contents()[2].rejected = true;
1442
1443 local_offer->AddGroup(bundle_group);
1444 remote_answer->AddGroup(bundle_group);
1445
1446 EXPECT_TRUE(transport_controller_
1447 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1448 .ok());
1449 EXPECT_TRUE(transport_controller_
1450 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1451 .ok());
1452
1453 // Verify the RtpTransport/DtlsTransport is destroyed correctly.
1454 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kVideoMid1));
1455 EXPECT_EQ(nullptr, transport_controller_->GetDtlsTransport(kDataMid1));
1456 // Verify the signals are fired correctly.
1457 auto it = changed_rtp_transport_by_mid_.find(kVideoMid1);
1458 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1459 EXPECT_EQ(nullptr, it->second);
1460 auto it2 = changed_dtls_transport_by_mid_.find(kDataMid1);
1461 ASSERT_TRUE(it2 != changed_dtls_transport_by_mid_.end());
1462 EXPECT_EQ(nullptr, it2->second);
1463}
1464
1465// Tests that changing the bundled MID in subsequent offer/answer exchange is
1466// not supported.
1467// TODO(bugs.webrtc.org/6704): Change this test to expect success once issue is
1468// fixed
1469TEST_F(JsepTransportControllerTest, ChangeBundledMidNotSupported) {
1470 CreateJsepTransportController(JsepTransportController::Config());
1471 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1472 bundle_group.AddContentName(kAudioMid1);
1473 bundle_group.AddContentName(kVideoMid1);
1474
Karl Wiberg918f50c2018-07-05 11:40:33 +02001475 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001476 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1477 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1478 nullptr);
1479 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1480 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1481 nullptr);
1482
Karl Wiberg918f50c2018-07-05 11:40:33 +02001483 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange818b6e2018-02-22 15:26:27 -08001484 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1485 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1486 nullptr);
1487 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1488 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1489 nullptr);
1490
1491 local_offer->AddGroup(bundle_group);
1492 remote_answer->AddGroup(bundle_group);
1493 EXPECT_TRUE(transport_controller_
1494 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1495 .ok());
1496 EXPECT_TRUE(transport_controller_
1497 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1498 .ok());
1499 EXPECT_EQ(transport_controller_->GetRtpTransport(kAudioMid1),
1500 transport_controller_->GetRtpTransport(kVideoMid1));
1501
1502 // Reorder the bundle group.
1503 EXPECT_TRUE(bundle_group.RemoveContentName(kAudioMid1));
1504 bundle_group.AddContentName(kAudioMid1);
1505 // The answerer uses the new bundle group and now the bundle mid is changed to
1506 // |kVideo1|.
1507 remote_answer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1508 remote_answer->AddGroup(bundle_group);
1509 EXPECT_TRUE(transport_controller_
1510 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1511 .ok());
1512 EXPECT_FALSE(transport_controller_
1513 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1514 .ok());
1515}
Zhi Huange830e682018-03-30 10:48:35 -07001516// Test that rejecting only the first m= section of a BUNDLE group is treated as
1517// an error, but rejecting all of them works as expected.
1518TEST_F(JsepTransportControllerTest, RejectFirstContentInBundleGroup) {
1519 CreateJsepTransportController(JsepTransportController::Config());
1520 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1521 bundle_group.AddContentName(kAudioMid1);
1522 bundle_group.AddContentName(kVideoMid1);
1523 bundle_group.AddContentName(kDataMid1);
1524
Karl Wiberg918f50c2018-07-05 11:40:33 +02001525 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange830e682018-03-30 10:48:35 -07001526 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1527 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1528 nullptr);
1529 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1530 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1531 nullptr);
1532 AddDataSection(local_offer.get(), kDataMid1,
1533 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1534 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1535 nullptr);
1536
Karl Wiberg918f50c2018-07-05 11:40:33 +02001537 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange830e682018-03-30 10:48:35 -07001538 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1539 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1540 nullptr);
1541 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1542 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1543 nullptr);
1544 AddDataSection(remote_answer.get(), kDataMid1,
1545 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1546 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1547 nullptr);
1548 // Reject audio content in answer.
1549 remote_answer->contents()[0].rejected = true;
1550
1551 local_offer->AddGroup(bundle_group);
1552 remote_answer->AddGroup(bundle_group);
1553
1554 EXPECT_TRUE(transport_controller_
1555 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1556 .ok());
1557 EXPECT_FALSE(transport_controller_
1558 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1559 .ok());
1560
1561 // Reject all the contents.
1562 remote_answer->contents()[1].rejected = true;
1563 remote_answer->contents()[2].rejected = true;
1564 EXPECT_TRUE(transport_controller_
1565 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1566 .ok());
1567 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kAudioMid1));
1568 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kVideoMid1));
1569 EXPECT_EQ(nullptr, transport_controller_->GetDtlsTransport(kDataMid1));
1570}
1571
1572// Tests that applying non-RTCP-mux offer would fail when kRtcpMuxPolicyRequire
1573// is used.
1574TEST_F(JsepTransportControllerTest, ApplyNonRtcpMuxOfferWhenMuxingRequired) {
1575 JsepTransportController::Config config;
1576 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
1577 CreateJsepTransportController(config);
Karl Wiberg918f50c2018-07-05 11:40:33 +02001578 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange830e682018-03-30 10:48:35 -07001579 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1580 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1581 nullptr);
1582
1583 local_offer->contents()[0].media_description()->set_rtcp_mux(false);
1584 // Applying a non-RTCP-mux offer is expected to fail.
1585 EXPECT_FALSE(transport_controller_
1586 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1587 .ok());
1588}
1589
1590// Tests that applying non-RTCP-mux answer would fail when kRtcpMuxPolicyRequire
1591// is used.
1592TEST_F(JsepTransportControllerTest, ApplyNonRtcpMuxAnswerWhenMuxingRequired) {
1593 JsepTransportController::Config config;
1594 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
1595 CreateJsepTransportController(config);
Karl Wiberg918f50c2018-07-05 11:40:33 +02001596 auto local_offer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange830e682018-03-30 10:48:35 -07001597 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1598 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1599 nullptr);
1600 EXPECT_TRUE(transport_controller_
1601 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1602 .ok());
1603
Karl Wiberg918f50c2018-07-05 11:40:33 +02001604 auto remote_answer = absl::make_unique<cricket::SessionDescription>();
Zhi Huange830e682018-03-30 10:48:35 -07001605 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1606 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1607 nullptr);
1608 // Applying a non-RTCP-mux answer is expected to fail.
1609 remote_answer->contents()[0].media_description()->set_rtcp_mux(false);
1610 EXPECT_FALSE(transport_controller_
1611 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1612 .ok());
1613}
Zhi Huange818b6e2018-02-22 15:26:27 -08001614
Zhi Huangd2248f82018-04-10 14:41:03 -07001615// This tests that the BUNDLE group in answer should be a subset of the offered
1616// group.
1617TEST_F(JsepTransportControllerTest,
1618 AddContentToBundleGroupInAnswerNotSupported) {
1619 CreateJsepTransportController(JsepTransportController::Config());
1620 auto local_offer = CreateSessionDescriptionWithoutBundle();
1621 auto remote_answer = CreateSessionDescriptionWithoutBundle();
1622
1623 cricket::ContentGroup offer_bundle_group(cricket::GROUP_TYPE_BUNDLE);
1624 offer_bundle_group.AddContentName(kAudioMid1);
1625 local_offer->AddGroup(offer_bundle_group);
1626
1627 cricket::ContentGroup answer_bundle_group(cricket::GROUP_TYPE_BUNDLE);
1628 answer_bundle_group.AddContentName(kAudioMid1);
1629 answer_bundle_group.AddContentName(kVideoMid1);
1630 remote_answer->AddGroup(answer_bundle_group);
1631 EXPECT_TRUE(transport_controller_
1632 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1633 .ok());
1634 EXPECT_FALSE(transport_controller_
1635 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1636 .ok());
1637}
1638
1639// This tests that the BUNDLE group with non-existing MID should be rejectd.
1640TEST_F(JsepTransportControllerTest, RejectBundleGroupWithNonExistingMid) {
1641 CreateJsepTransportController(JsepTransportController::Config());
1642 auto local_offer = CreateSessionDescriptionWithoutBundle();
1643 auto remote_answer = CreateSessionDescriptionWithoutBundle();
1644
1645 cricket::ContentGroup invalid_bundle_group(cricket::GROUP_TYPE_BUNDLE);
1646 // The BUNDLE group is invalid because there is no data section in the
1647 // description.
1648 invalid_bundle_group.AddContentName(kDataMid1);
1649 local_offer->AddGroup(invalid_bundle_group);
1650 remote_answer->AddGroup(invalid_bundle_group);
1651
1652 EXPECT_FALSE(transport_controller_
1653 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1654 .ok());
1655 EXPECT_FALSE(transport_controller_
1656 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1657 .ok());
1658}
1659
1660// This tests that an answer shouldn't be able to remove an m= section from an
1661// established group without rejecting it.
1662TEST_F(JsepTransportControllerTest, RemoveContentFromBundleGroup) {
1663 CreateJsepTransportController(JsepTransportController::Config());
1664
1665 auto local_offer = CreateSessionDescriptionWithBundleGroup();
1666 auto remote_answer = CreateSessionDescriptionWithBundleGroup();
1667 EXPECT_TRUE(transport_controller_
1668 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1669 .ok());
1670 EXPECT_TRUE(transport_controller_
1671 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1672 .ok());
1673
1674 // Do an re-offer/answer.
1675 EXPECT_TRUE(transport_controller_
1676 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1677 .ok());
1678 auto new_answer = CreateSessionDescriptionWithoutBundle();
1679 cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
1680 // The answer removes video from the BUNDLE group without rejecting it is
1681 // invalid.
1682 new_bundle_group.AddContentName(kAudioMid1);
1683 new_answer->AddGroup(new_bundle_group);
1684
1685 // Applying invalid answer is expected to fail.
1686 EXPECT_FALSE(transport_controller_
1687 ->SetRemoteDescription(SdpType::kAnswer, new_answer.get())
1688 .ok());
1689
1690 // Rejected the video content.
1691 auto video_content = new_answer->GetContentByName(kVideoMid1);
1692 ASSERT_TRUE(video_content);
1693 video_content->rejected = true;
1694 EXPECT_TRUE(transport_controller_
1695 ->SetRemoteDescription(SdpType::kAnswer, new_answer.get())
1696 .ok());
1697}
1698
Zhi Huange818b6e2018-02-22 15:26:27 -08001699} // namespace webrtc