blob: 3ab9acb2679d6ac093d87ca22726d4d0f386286c [file] [log] [blame]
Steve Antonf1c6db12017-10-13 11:13:35 -07001/*
2 * Copyright 2017 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 "p2p/base/fakeportallocator.h"
12#include "p2p/base/teststunserver.h"
13#include "p2p/client/basicportallocator.h"
14#include "pc/mediasession.h"
15#include "pc/peerconnectionwrapper.h"
16#include "pc/sdputils.h"
17#ifdef WEBRTC_ANDROID
18#include "pc/test/androidtestinitializer.h"
19#endif
Karl Wiberg1b0eae32017-10-17 14:48:54 +020020#include "api/audio_codecs/builtin_audio_decoder_factory.h"
21#include "api/audio_codecs/builtin_audio_encoder_factory.h"
Steve Antonf1c6db12017-10-13 11:13:35 -070022#include "pc/test/fakeaudiocapturemodule.h"
23#include "rtc_base/fakenetwork.h"
24#include "rtc_base/gunit.h"
25#include "rtc_base/ptr_util.h"
26#include "rtc_base/virtualsocketserver.h"
27
28namespace webrtc {
29
30using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
31using RTCOfferAnswerOptions = PeerConnectionInterface::RTCOfferAnswerOptions;
32using rtc::SocketAddress;
33using ::testing::Values;
34
35constexpr int kIceCandidatesTimeout = 10000;
36
37class PeerConnectionWrapperForIceUnitTest : public PeerConnectionWrapper {
38 public:
39 using PeerConnectionWrapper::PeerConnectionWrapper;
40
41 // Adds a new ICE candidate to the first transport.
42 bool AddIceCandidate(cricket::Candidate* candidate) {
43 RTC_DCHECK(pc()->remote_description());
44 const auto* desc = pc()->remote_description()->description();
45 RTC_DCHECK(desc->contents().size() > 0);
46 const auto& first_content = desc->contents()[0];
47 candidate->set_transport_name(first_content.name);
48 JsepIceCandidate jsep_candidate(first_content.name, 0, *candidate);
49 return pc()->AddIceCandidate(&jsep_candidate);
50 }
51
52 // Returns ICE candidates from the remote session description.
53 std::vector<const IceCandidateInterface*>
54 GetIceCandidatesFromRemoteDescription() {
55 const SessionDescriptionInterface* sdesc = pc()->remote_description();
56 RTC_DCHECK(sdesc);
57 std::vector<const IceCandidateInterface*> candidates;
58 for (size_t mline_index = 0; mline_index < sdesc->number_of_mediasections();
59 mline_index++) {
60 const auto* candidate_collection = sdesc->candidates(mline_index);
61 for (size_t i = 0; i < candidate_collection->count(); i++) {
62 candidates.push_back(candidate_collection->at(i));
63 }
64 }
65 return candidates;
66 }
67
68 rtc::FakeNetworkManager* network() { return network_; }
69
70 void set_network(rtc::FakeNetworkManager* network) { network_ = network; }
71
72 private:
73 rtc::FakeNetworkManager* network_;
74};
75
76class PeerConnectionIceUnitTest : public ::testing::Test {
77 protected:
78 typedef std::unique_ptr<PeerConnectionWrapperForIceUnitTest> WrapperPtr;
79
80 PeerConnectionIceUnitTest()
81 : vss_(new rtc::VirtualSocketServer()), main_(vss_.get()) {
82#ifdef WEBRTC_ANDROID
83 InitializeAndroidObjects();
84#endif
85 pc_factory_ = CreatePeerConnectionFactory(
86 rtc::Thread::Current(), rtc::Thread::Current(), rtc::Thread::Current(),
Karl Wiberg1b0eae32017-10-17 14:48:54 +020087 FakeAudioCaptureModule::Create(), CreateBuiltinAudioEncoderFactory(),
88 CreateBuiltinAudioDecoderFactory(), nullptr, nullptr);
Steve Antonf1c6db12017-10-13 11:13:35 -070089 }
90
91 WrapperPtr CreatePeerConnection() {
92 return CreatePeerConnection(RTCConfiguration());
93 }
94
95 WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
96 auto* fake_network = NewFakeNetwork();
97 auto port_allocator =
98 rtc::MakeUnique<cricket::BasicPortAllocator>(fake_network);
99 port_allocator->set_flags(cricket::PORTALLOCATOR_DISABLE_TCP |
100 cricket::PORTALLOCATOR_DISABLE_RELAY);
101 port_allocator->set_step_delay(cricket::kMinimumStepDelay);
102 auto observer = rtc::MakeUnique<MockPeerConnectionObserver>();
103 auto pc = pc_factory_->CreatePeerConnection(
104 config, std::move(port_allocator), nullptr, observer.get());
105 if (!pc) {
106 return nullptr;
107 }
108
109 auto wrapper = rtc::MakeUnique<PeerConnectionWrapperForIceUnitTest>(
110 pc_factory_, pc, std::move(observer));
111 wrapper->set_network(fake_network);
112 return wrapper;
113 }
114
115 // Accepts the same arguments as CreatePeerConnection and adds default audio
116 // and video tracks.
117 template <typename... Args>
118 WrapperPtr CreatePeerConnectionWithAudioVideo(Args&&... args) {
119 auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
120 if (!wrapper) {
121 return nullptr;
122 }
Steve Anton8d3444d2017-10-20 15:30:51 -0700123 wrapper->AddAudioTrack("a");
124 wrapper->AddVideoTrack("v");
Steve Antonf1c6db12017-10-13 11:13:35 -0700125 return wrapper;
126 }
127
128 cricket::Candidate CreateLocalUdpCandidate(
129 const rtc::SocketAddress& address) {
130 cricket::Candidate candidate;
131 candidate.set_component(cricket::ICE_CANDIDATE_COMPONENT_DEFAULT);
132 candidate.set_protocol(cricket::UDP_PROTOCOL_NAME);
133 candidate.set_address(address);
134 candidate.set_type(cricket::LOCAL_PORT_TYPE);
135 return candidate;
136 }
137
138 // Remove all ICE ufrag/pwd lines from the given session description.
139 void RemoveIceUfragPwd(SessionDescriptionInterface* sdesc) {
140 SetIceUfragPwd(sdesc, "", "");
141 }
142
143 // Sets all ICE ufrag/pwds on the given session description.
144 void SetIceUfragPwd(SessionDescriptionInterface* sdesc,
145 const std::string& ufrag,
146 const std::string& pwd) {
147 auto* desc = sdesc->description();
148 for (const auto& content : desc->contents()) {
149 auto* transport_info = desc->GetTransportInfoByName(content.name);
150 transport_info->description.ice_ufrag = ufrag;
151 transport_info->description.ice_pwd = pwd;
152 }
153 }
154
155 cricket::TransportDescription* GetFirstTransportDescription(
156 SessionDescriptionInterface* sdesc) {
157 auto* desc = sdesc->description();
158 RTC_DCHECK(desc->contents().size() > 0);
159 auto* transport_info =
160 desc->GetTransportInfoByName(desc->contents()[0].name);
161 RTC_DCHECK(transport_info);
162 return &transport_info->description;
163 }
164
165 const cricket::TransportDescription* GetFirstTransportDescription(
166 const SessionDescriptionInterface* sdesc) {
167 auto* desc = sdesc->description();
168 RTC_DCHECK(desc->contents().size() > 0);
169 auto* transport_info =
170 desc->GetTransportInfoByName(desc->contents()[0].name);
171 RTC_DCHECK(transport_info);
172 return &transport_info->description;
173 }
174
175 bool AddCandidateToFirstTransport(cricket::Candidate* candidate,
176 SessionDescriptionInterface* sdesc) {
177 auto* desc = sdesc->description();
178 RTC_DCHECK(desc->contents().size() > 0);
179 const auto& first_content = desc->contents()[0];
180 candidate->set_transport_name(first_content.name);
181 JsepIceCandidate jsep_candidate(first_content.name, 0, *candidate);
182 return sdesc->AddCandidate(&jsep_candidate);
183 }
184
185 rtc::FakeNetworkManager* NewFakeNetwork() {
186 // The PeerConnection's port allocator is tied to the PeerConnection's
187 // lifetime and expects the underlying NetworkManager to outlive it. That
188 // prevents us from having the PeerConnectionWrapper own the fake network.
189 // Therefore, the test fixture will own all the fake networks even though
190 // tests should access the fake network through the PeerConnectionWrapper.
191 auto* fake_network = new rtc::FakeNetworkManager();
192 fake_networks_.emplace_back(fake_network);
193 return fake_network;
194 }
195
196 std::unique_ptr<rtc::VirtualSocketServer> vss_;
197 rtc::AutoSocketServerThread main_;
198 rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
199 std::vector<std::unique_ptr<rtc::FakeNetworkManager>> fake_networks_;
200};
201
202::testing::AssertionResult AssertCandidatesEqual(const char* a_expr,
203 const char* b_expr,
204 const cricket::Candidate& a,
205 const cricket::Candidate& b) {
206 std::stringstream failure_info;
207 if (a.component() != b.component()) {
208 failure_info << "\ncomponent: " << a.component() << " != " << b.component();
209 }
210 if (a.protocol() != b.protocol()) {
211 failure_info << "\nprotocol: " << a.protocol() << " != " << b.protocol();
212 }
213 if (a.address() != b.address()) {
214 failure_info << "\naddress: " << a.address().ToString()
215 << " != " << b.address().ToString();
216 }
217 if (a.type() != b.type()) {
218 failure_info << "\ntype: " << a.type() << " != " << b.type();
219 }
220 std::string failure_info_str = failure_info.str();
221 if (failure_info_str.empty()) {
222 return ::testing::AssertionSuccess();
223 } else {
224 return ::testing::AssertionFailure()
225 << a_expr << " and " << b_expr << " are not equal"
226 << failure_info_str;
227 }
228}
229
230TEST_F(PeerConnectionIceUnitTest, OfferContainsGatheredCandidates) {
231 const SocketAddress kLocalAddress("1.1.1.1", 0);
232
233 auto caller = CreatePeerConnectionWithAudioVideo();
234 caller->network()->AddInterface(kLocalAddress);
235
236 // Start ICE candidate gathering by setting the local offer.
237 ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
238
239 EXPECT_TRUE_WAIT(caller->IsIceGatheringDone(), kIceCandidatesTimeout);
240
241 auto offer = caller->CreateOffer();
242 EXPECT_LT(0u, caller->observer()->GetCandidatesByMline(0).size());
243 EXPECT_EQ(caller->observer()->GetCandidatesByMline(0).size(),
244 offer->candidates(0)->count());
245 EXPECT_LT(0u, caller->observer()->GetCandidatesByMline(1).size());
246 EXPECT_EQ(caller->observer()->GetCandidatesByMline(1).size(),
247 offer->candidates(1)->count());
248}
249
250TEST_F(PeerConnectionIceUnitTest, AnswerContainsGatheredCandidates) {
251 const SocketAddress kCallerAddress("1.1.1.1", 0);
252
253 auto caller = CreatePeerConnectionWithAudioVideo();
254 auto callee = CreatePeerConnectionWithAudioVideo();
255 caller->network()->AddInterface(kCallerAddress);
256
257 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
258 ASSERT_TRUE(callee->SetLocalDescription(callee->CreateAnswer()));
259
260 EXPECT_TRUE_WAIT(callee->IsIceGatheringDone(), kIceCandidatesTimeout);
261
262 auto answer = callee->CreateAnswer();
263 EXPECT_LT(0u, caller->observer()->GetCandidatesByMline(0).size());
264 EXPECT_EQ(callee->observer()->GetCandidatesByMline(0).size(),
265 answer->candidates(0)->count());
266 EXPECT_LT(0u, caller->observer()->GetCandidatesByMline(1).size());
267 EXPECT_EQ(callee->observer()->GetCandidatesByMline(1).size(),
268 answer->candidates(1)->count());
269}
270
271TEST_F(PeerConnectionIceUnitTest,
272 CanSetRemoteSessionDescriptionWithRemoteCandidates) {
273 const SocketAddress kCallerAddress("1.1.1.1", 1111);
274
275 auto caller = CreatePeerConnectionWithAudioVideo();
276 auto callee = CreatePeerConnectionWithAudioVideo();
277
278 auto offer = caller->CreateOfferAndSetAsLocal();
279 cricket::Candidate candidate = CreateLocalUdpCandidate(kCallerAddress);
280 AddCandidateToFirstTransport(&candidate, offer.get());
281
282 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
283 auto remote_candidates = callee->GetIceCandidatesFromRemoteDescription();
284 ASSERT_EQ(1u, remote_candidates.size());
285 EXPECT_PRED_FORMAT2(AssertCandidatesEqual, candidate,
286 remote_candidates[0]->candidate());
287}
288
289TEST_F(PeerConnectionIceUnitTest, SetLocalDescriptionFailsIfNoIceCredentials) {
290 auto caller = CreatePeerConnectionWithAudioVideo();
291
292 auto offer = caller->CreateOffer();
293 RemoveIceUfragPwd(offer.get());
294
295 EXPECT_FALSE(caller->SetLocalDescription(std::move(offer)));
296}
297
298TEST_F(PeerConnectionIceUnitTest, SetRemoteDescriptionFailsIfNoIceCredentials) {
299 auto caller = CreatePeerConnectionWithAudioVideo();
300 auto callee = CreatePeerConnectionWithAudioVideo();
301
302 auto offer = caller->CreateOfferAndSetAsLocal();
303 RemoveIceUfragPwd(offer.get());
304
305 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
306}
307
308// The following group tests that ICE candidates are not generated before
309// SetLocalDescription is called on a PeerConnection.
310
311TEST_F(PeerConnectionIceUnitTest, NoIceCandidatesBeforeSetLocalDescription) {
312 const SocketAddress kLocalAddress("1.1.1.1", 0);
313
314 auto caller = CreatePeerConnectionWithAudioVideo();
315 caller->network()->AddInterface(kLocalAddress);
316
317 // Pump for 1 second and verify that no candidates are generated.
318 rtc::Thread::Current()->ProcessMessages(1000);
319
320 EXPECT_EQ(0u, caller->observer()->candidates_.size());
321}
322TEST_F(PeerConnectionIceUnitTest,
323 NoIceCandidatesBeforeAnswerSetAsLocalDescription) {
324 const SocketAddress kCallerAddress("1.1.1.1", 1111);
325
326 auto caller = CreatePeerConnectionWithAudioVideo();
327 auto callee = CreatePeerConnectionWithAudioVideo();
328 caller->network()->AddInterface(kCallerAddress);
329
330 auto offer = caller->CreateOfferAndSetAsLocal();
331 cricket::Candidate candidate = CreateLocalUdpCandidate(kCallerAddress);
332 AddCandidateToFirstTransport(&candidate, offer.get());
333 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
334
335 // Pump for 1 second and verify that no candidates are generated.
336 rtc::Thread::Current()->ProcessMessages(1000);
337
338 EXPECT_EQ(0u, callee->observer()->candidates_.size());
339}
340
341TEST_F(PeerConnectionIceUnitTest,
342 CannotAddCandidateWhenRemoteDescriptionNotSet) {
343 const SocketAddress kCalleeAddress("1.1.1.1", 1111);
344
345 auto caller = CreatePeerConnectionWithAudioVideo();
346 cricket::Candidate candidate = CreateLocalUdpCandidate(kCalleeAddress);
347 JsepIceCandidate jsep_candidate(cricket::CN_AUDIO, 0, candidate);
348
349 EXPECT_FALSE(caller->pc()->AddIceCandidate(&jsep_candidate));
350
351 caller->CreateOfferAndSetAsLocal();
352
353 EXPECT_FALSE(caller->pc()->AddIceCandidate(&jsep_candidate));
354}
355
356TEST_F(PeerConnectionIceUnitTest, DuplicateIceCandidateIgnoredWhenAdded) {
357 const SocketAddress kCalleeAddress("1.1.1.1", 1111);
358
359 auto caller = CreatePeerConnectionWithAudioVideo();
360 auto callee = CreatePeerConnectionWithAudioVideo();
361
362 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
363 ASSERT_TRUE(
364 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
365
366 cricket::Candidate candidate = CreateLocalUdpCandidate(kCalleeAddress);
367 caller->AddIceCandidate(&candidate);
368 EXPECT_TRUE(caller->AddIceCandidate(&candidate));
369 EXPECT_EQ(1u, caller->GetIceCandidatesFromRemoteDescription().size());
370}
371
372TEST_F(PeerConnectionIceUnitTest,
373 AddRemoveCandidateWithEmptyTransportDoesNotCrash) {
374 const SocketAddress kCalleeAddress("1.1.1.1", 1111);
375
376 auto caller = CreatePeerConnectionWithAudioVideo();
377 auto callee = CreatePeerConnectionWithAudioVideo();
378
379 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
380 ASSERT_TRUE(
381 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
382
383 // |candidate.transport_name()| is empty.
384 cricket::Candidate candidate = CreateLocalUdpCandidate(kCalleeAddress);
385 JsepIceCandidate ice_candidate(cricket::CN_AUDIO, 0, candidate);
386 EXPECT_TRUE(caller->pc()->AddIceCandidate(&ice_candidate));
387 EXPECT_TRUE(caller->pc()->RemoveIceCandidates({candidate}));
388}
389
390TEST_F(PeerConnectionIceUnitTest, RemoveCandidateRemovesFromRemoteDescription) {
391 const SocketAddress kCalleeAddress("1.1.1.1", 1111);
392
393 auto caller = CreatePeerConnectionWithAudioVideo();
394 auto callee = CreatePeerConnectionWithAudioVideo();
395
396 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
397 ASSERT_TRUE(
398 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
399
400 cricket::Candidate candidate = CreateLocalUdpCandidate(kCalleeAddress);
401 ASSERT_TRUE(caller->AddIceCandidate(&candidate));
402 EXPECT_TRUE(caller->pc()->RemoveIceCandidates({candidate}));
403 EXPECT_EQ(0u, caller->GetIceCandidatesFromRemoteDescription().size());
404}
405
406// Test that if a candidate is added via AddIceCandidate and via an updated
407// remote description, then both candidates appear in the stored remote
408// description.
409TEST_F(PeerConnectionIceUnitTest,
410 CandidateInSubsequentOfferIsAddedToRemoteDescription) {
411 const SocketAddress kCallerAddress1("1.1.1.1", 1111);
412 const SocketAddress kCallerAddress2("2.2.2.2", 2222);
413
414 auto caller = CreatePeerConnectionWithAudioVideo();
415 auto callee = CreatePeerConnectionWithAudioVideo();
416
417 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
418 ASSERT_TRUE(
419 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
420
421 // Add one candidate via |AddIceCandidate|.
422 cricket::Candidate candidate1 = CreateLocalUdpCandidate(kCallerAddress1);
423 ASSERT_TRUE(callee->AddIceCandidate(&candidate1));
424
425 // Add the second candidate via a reoffer.
426 auto offer = caller->CreateOffer();
427 cricket::Candidate candidate2 = CreateLocalUdpCandidate(kCallerAddress2);
428 AddCandidateToFirstTransport(&candidate2, offer.get());
429
430 // Expect both candidates to appear in the callee's remote description.
431 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
432 EXPECT_EQ(2u, callee->GetIceCandidatesFromRemoteDescription().size());
433}
434
435// The follow test verifies that SetLocal/RemoteDescription fails when an offer
436// has either ICE ufrag/pwd too short or too long and succeeds otherwise.
437// The standard (https://tools.ietf.org/html/rfc5245#section-15.4) says that
438// pwd must be 22-256 characters and ufrag must be 4-256 characters.
439TEST_F(PeerConnectionIceUnitTest, VerifyUfragPwdLength) {
440 auto caller = CreatePeerConnectionWithAudioVideo();
441 auto callee = CreatePeerConnectionWithAudioVideo();
442
443 auto set_local_description_with_ufrag_pwd_length =
444 [this, &caller](int ufrag_len, int pwd_len) {
445 auto offer = caller->CreateOffer();
446 SetIceUfragPwd(offer.get(), std::string(ufrag_len, 'x'),
447 std::string(pwd_len, 'x'));
448 return caller->SetLocalDescription(std::move(offer));
449 };
450
451 auto set_remote_description_with_ufrag_pwd_length =
452 [this, &caller, &callee](int ufrag_len, int pwd_len) {
453 auto offer = caller->CreateOffer();
454 SetIceUfragPwd(offer.get(), std::string(ufrag_len, 'x'),
455 std::string(pwd_len, 'x'));
456 return callee->SetRemoteDescription(std::move(offer));
457 };
458
459 EXPECT_FALSE(set_local_description_with_ufrag_pwd_length(3, 22));
460 EXPECT_FALSE(set_remote_description_with_ufrag_pwd_length(3, 22));
461 EXPECT_FALSE(set_local_description_with_ufrag_pwd_length(257, 22));
462 EXPECT_FALSE(set_remote_description_with_ufrag_pwd_length(257, 22));
463 EXPECT_FALSE(set_local_description_with_ufrag_pwd_length(4, 21));
464 EXPECT_FALSE(set_remote_description_with_ufrag_pwd_length(4, 21));
465 EXPECT_FALSE(set_local_description_with_ufrag_pwd_length(4, 257));
466 EXPECT_FALSE(set_remote_description_with_ufrag_pwd_length(4, 257));
467 EXPECT_TRUE(set_local_description_with_ufrag_pwd_length(4, 22));
468 EXPECT_TRUE(set_remote_description_with_ufrag_pwd_length(4, 22));
469 EXPECT_TRUE(set_local_description_with_ufrag_pwd_length(256, 256));
470 EXPECT_TRUE(set_remote_description_with_ufrag_pwd_length(256, 256));
471}
472
473::testing::AssertionResult AssertIpInCandidates(
474 const char* address_expr,
475 const char* candidates_expr,
476 const SocketAddress& address,
477 const std::vector<IceCandidateInterface*> candidates) {
478 std::stringstream candidate_hosts;
479 for (const auto* candidate : candidates) {
480 const auto& candidate_ip = candidate->candidate().address().ipaddr();
481 if (candidate_ip == address.ipaddr()) {
482 return ::testing::AssertionSuccess();
483 }
484 candidate_hosts << "\n" << candidate_ip;
485 }
486 return ::testing::AssertionFailure()
487 << address_expr << " (host " << address.HostAsURIString()
488 << ") not in " << candidates_expr
489 << " which have the following address hosts:" << candidate_hosts.str();
490}
491
492TEST_F(PeerConnectionIceUnitTest, CandidatesGeneratedForEachLocalInterface) {
493 const SocketAddress kLocalAddress1("1.1.1.1", 0);
494 const SocketAddress kLocalAddress2("2.2.2.2", 0);
495
496 auto caller = CreatePeerConnectionWithAudioVideo();
497 caller->network()->AddInterface(kLocalAddress1);
498 caller->network()->AddInterface(kLocalAddress2);
499
500 caller->CreateOfferAndSetAsLocal();
501 EXPECT_TRUE_WAIT(caller->IsIceGatheringDone(), kIceCandidatesTimeout);
502
503 auto candidates = caller->observer()->GetCandidatesByMline(0);
504 EXPECT_PRED_FORMAT2(AssertIpInCandidates, kLocalAddress1, candidates);
505 EXPECT_PRED_FORMAT2(AssertIpInCandidates, kLocalAddress2, candidates);
506}
507
508TEST_F(PeerConnectionIceUnitTest,
509 TrickledSingleCandidateAddedToRemoteDescription) {
510 const SocketAddress kCallerAddress("1.1.1.1", 1111);
511
512 auto caller = CreatePeerConnectionWithAudioVideo();
513 auto callee = CreatePeerConnectionWithAudioVideo();
514
515 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
516
517 cricket::Candidate candidate = CreateLocalUdpCandidate(kCallerAddress);
518 callee->AddIceCandidate(&candidate);
519 auto candidates = callee->GetIceCandidatesFromRemoteDescription();
520 ASSERT_EQ(1u, candidates.size());
521 EXPECT_PRED_FORMAT2(AssertCandidatesEqual, candidate,
522 candidates[0]->candidate());
523}
524
525TEST_F(PeerConnectionIceUnitTest,
526 TwoTrickledCandidatesAddedToRemoteDescription) {
527 const SocketAddress kCalleeAddress1("1.1.1.1", 1111);
528 const SocketAddress kCalleeAddress2("2.2.2.2", 2222);
529
530 auto caller = CreatePeerConnectionWithAudioVideo();
531 auto callee = CreatePeerConnectionWithAudioVideo();
532
533 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
534 ASSERT_TRUE(
535 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
536
537 cricket::Candidate candidate1 = CreateLocalUdpCandidate(kCalleeAddress1);
538 caller->AddIceCandidate(&candidate1);
539
540 cricket::Candidate candidate2 = CreateLocalUdpCandidate(kCalleeAddress2);
541 caller->AddIceCandidate(&candidate2);
542
543 auto candidates = caller->GetIceCandidatesFromRemoteDescription();
544 ASSERT_EQ(2u, candidates.size());
545 EXPECT_PRED_FORMAT2(AssertCandidatesEqual, candidate1,
546 candidates[0]->candidate());
547 EXPECT_PRED_FORMAT2(AssertCandidatesEqual, candidate2,
548 candidates[1]->candidate());
549}
550
551TEST_F(PeerConnectionIceUnitTest,
552 LocalDescriptionUpdatedWhenContinualGathering) {
553 const SocketAddress kLocalAddress("1.1.1.1", 0);
554
555 RTCConfiguration config;
556 config.continual_gathering_policy =
557 PeerConnectionInterface::GATHER_CONTINUALLY;
558 auto caller = CreatePeerConnectionWithAudioVideo(config);
559 caller->network()->AddInterface(kLocalAddress);
560
561 // Start ICE candidate gathering by setting the local offer.
562 ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
563
564 // Since we're using continual gathering, we won't get "gathering done".
565 EXPECT_TRUE_WAIT(
566 caller->pc()->local_description()->candidates(0)->count() > 0,
567 kIceCandidatesTimeout);
568}
569
570// Test that when continual gathering is enabled, and a network interface goes
571// down, the candidate is signaled as removed and removed from the local
572// description.
573TEST_F(PeerConnectionIceUnitTest,
574 LocalCandidatesRemovedWhenNetworkDownIfGatheringContinually) {
575 const SocketAddress kLocalAddress("1.1.1.1", 0);
576
577 RTCConfiguration config;
578 config.continual_gathering_policy =
579 PeerConnectionInterface::GATHER_CONTINUALLY;
580 auto caller = CreatePeerConnectionWithAudioVideo(config);
581 caller->network()->AddInterface(kLocalAddress);
582
583 // Start ICE candidate gathering by setting the local offer.
584 ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
585
586 EXPECT_TRUE_WAIT(
587 caller->pc()->local_description()->candidates(0)->count() > 0,
588 kIceCandidatesTimeout);
589
590 // Remove the only network interface, causing the PeerConnection to signal
591 // the removal of all candidates derived from this interface.
592 caller->network()->RemoveInterface(kLocalAddress);
593
594 EXPECT_EQ_WAIT(0u, caller->pc()->local_description()->candidates(0)->count(),
595 kIceCandidatesTimeout);
596 EXPECT_LT(0, caller->observer()->num_candidates_removed_);
597}
598
599TEST_F(PeerConnectionIceUnitTest,
600 LocalCandidatesNotRemovedWhenNetworkDownIfGatheringOnce) {
601 const SocketAddress kLocalAddress("1.1.1.1", 0);
602
603 RTCConfiguration config;
604 config.continual_gathering_policy = PeerConnectionInterface::GATHER_ONCE;
605 auto caller = CreatePeerConnectionWithAudioVideo(config);
606 caller->network()->AddInterface(kLocalAddress);
607
608 // Start ICE candidate gathering by setting the local offer.
609 ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
610
611 EXPECT_TRUE_WAIT(caller->IsIceGatheringDone(), kIceCandidatesTimeout);
612
613 caller->network()->RemoveInterface(kLocalAddress);
614
615 // Verify that the local candidates are not removed;
616 rtc::Thread::Current()->ProcessMessages(1000);
617 EXPECT_EQ(0, caller->observer()->num_candidates_removed_);
618}
619
620// The following group tests that when an offer includes a new ufrag or pwd
621// (indicating an ICE restart) the old candidates are removed and new candidates
622// added to the remote description.
623
624TEST_F(PeerConnectionIceUnitTest, IceRestartOfferClearsExistingCandidate) {
625 const SocketAddress kCallerAddress("1.1.1.1", 1111);
626
627 auto caller = CreatePeerConnectionWithAudioVideo();
628 auto callee = CreatePeerConnectionWithAudioVideo();
629
630 auto offer = caller->CreateOffer();
631 cricket::Candidate candidate = CreateLocalUdpCandidate(kCallerAddress);
632 AddCandidateToFirstTransport(&candidate, offer.get());
633
634 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
635
636 RTCOfferAnswerOptions options;
637 options.ice_restart = true;
638 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOffer(options)));
639
640 EXPECT_EQ(0u, callee->GetIceCandidatesFromRemoteDescription().size());
641}
642TEST_F(PeerConnectionIceUnitTest,
643 IceRestartOfferCandidateReplacesExistingCandidate) {
644 const SocketAddress kFirstCallerAddress("1.1.1.1", 1111);
645 const SocketAddress kRestartedCallerAddress("2.2.2.2", 2222);
646
647 auto caller = CreatePeerConnectionWithAudioVideo();
648 auto callee = CreatePeerConnectionWithAudioVideo();
649
650 auto offer = caller->CreateOffer();
651 cricket::Candidate old_candidate =
652 CreateLocalUdpCandidate(kFirstCallerAddress);
653 AddCandidateToFirstTransport(&old_candidate, offer.get());
654
655 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
656
657 RTCOfferAnswerOptions options;
658 options.ice_restart = true;
659 auto restart_offer = caller->CreateOffer(options);
660 cricket::Candidate new_candidate =
661 CreateLocalUdpCandidate(kRestartedCallerAddress);
662 AddCandidateToFirstTransport(&new_candidate, restart_offer.get());
663
664 ASSERT_TRUE(callee->SetRemoteDescription(std::move(restart_offer)));
665
666 auto remote_candidates = callee->GetIceCandidatesFromRemoteDescription();
667 ASSERT_EQ(1u, remote_candidates.size());
668 EXPECT_PRED_FORMAT2(AssertCandidatesEqual, new_candidate,
669 remote_candidates[0]->candidate());
670}
671
672// Test that if there is not an ICE restart (i.e., nothing changes), then the
673// answer to a later offer should have the same ufrag/pwd as the first answer.
674TEST_F(PeerConnectionIceUnitTest,
675 LaterAnswerHasSameIceCredentialsIfNoIceRestart) {
676 auto caller = CreatePeerConnectionWithAudioVideo();
677 auto callee = CreatePeerConnectionWithAudioVideo();
678
679 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
680 ASSERT_TRUE(
681 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
682
683 // Re-offer.
684 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
685
686 auto answer = callee->CreateAnswer();
687 auto* answer_transport_desc = GetFirstTransportDescription(answer.get());
688 auto* local_transport_desc =
689 GetFirstTransportDescription(callee->pc()->local_description());
690
691 EXPECT_EQ(answer_transport_desc->ice_ufrag, local_transport_desc->ice_ufrag);
692 EXPECT_EQ(answer_transport_desc->ice_pwd, local_transport_desc->ice_pwd);
693}
694
695// The following parameterized test verifies that if an offer is sent with a
696// modified ICE ufrag and/or ICE pwd, then the answer should identify that the
697// other side has initiated an ICE restart and generate a new ufrag and pwd.
698// RFC 5245 says: "If the offer contained a change in the a=ice-ufrag or
699// a=ice-pwd attributes compared to the previous SDP from the peer, it
700// indicates that ICE is restarting for this media stream."
701
702class PeerConnectionIceUfragPwdAnswerUnitTest
703 : public PeerConnectionIceUnitTest,
704 public ::testing::WithParamInterface<std::pair<bool, bool>> {
705 protected:
706 PeerConnectionIceUfragPwdAnswerUnitTest() {
707 offer_new_ufrag_ = GetParam().first;
708 offer_new_pwd_ = GetParam().second;
709 }
710
711 bool offer_new_ufrag_;
712 bool offer_new_pwd_;
713};
714
715TEST_P(PeerConnectionIceUfragPwdAnswerUnitTest, TestIncludedInAnswer) {
716 auto caller = CreatePeerConnectionWithAudioVideo();
717 auto callee = CreatePeerConnectionWithAudioVideo();
718
719 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
720 ASSERT_TRUE(
721 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
722
723 auto offer = caller->CreateOffer();
724 auto* offer_transport_desc = GetFirstTransportDescription(offer.get());
725 if (offer_new_ufrag_) {
726 offer_transport_desc->ice_ufrag += "_new";
727 }
728 if (offer_new_pwd_) {
729 offer_transport_desc->ice_pwd += "_new";
730 }
731
732 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
733
734 auto answer = callee->CreateAnswer();
735 auto* answer_transport_desc = GetFirstTransportDescription(answer.get());
736 auto* local_transport_desc =
737 GetFirstTransportDescription(callee->pc()->local_description());
738
739 EXPECT_NE(answer_transport_desc->ice_ufrag, local_transport_desc->ice_ufrag);
740 EXPECT_NE(answer_transport_desc->ice_pwd, local_transport_desc->ice_pwd);
741}
742
743INSTANTIATE_TEST_CASE_P(
744 PeerConnectionIceUnitTest,
745 PeerConnectionIceUfragPwdAnswerUnitTest,
746 Values(std::make_pair(true, true), // Both changed.
747 std::make_pair(true, false), // Only ufrag changed.
748 std::make_pair(false, true))); // Only pwd changed.
749
750// Test that if an ICE restart is offered on one media section, then the answer
751// will only change ICE ufrag/pwd for that section and keep the other sections
752// the same.
753// Note that this only works if we have disabled BUNDLE, otherwise all media
754// sections will share the same transport.
755TEST_F(PeerConnectionIceUnitTest,
756 CreateAnswerHasNewUfragPwdForOnlyMediaSectionWhichRestarted) {
757 auto caller = CreatePeerConnectionWithAudioVideo();
758 auto callee = CreatePeerConnectionWithAudioVideo();
759
760 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
761 ASSERT_TRUE(
762 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
763
764 RTCOfferAnswerOptions disable_bundle_options;
765 disable_bundle_options.use_rtp_mux = false;
766
767 auto offer = caller->CreateOffer(disable_bundle_options);
768
769 // Signal ICE restart on the first media section.
770 auto* offer_transport_desc = GetFirstTransportDescription(offer.get());
771 offer_transport_desc->ice_ufrag += "_new";
772 offer_transport_desc->ice_pwd += "_new";
773
774 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
775
776 auto answer = callee->CreateAnswer(disable_bundle_options);
777 const auto& answer_transports = answer->description()->transport_infos();
778 const auto& local_transports =
779 callee->pc()->local_description()->description()->transport_infos();
780
781 EXPECT_NE(answer_transports[0].description.ice_ufrag,
782 local_transports[0].description.ice_ufrag);
783 EXPECT_NE(answer_transports[0].description.ice_pwd,
784 local_transports[0].description.ice_pwd);
785 EXPECT_EQ(answer_transports[1].description.ice_ufrag,
786 local_transports[1].description.ice_ufrag);
787 EXPECT_EQ(answer_transports[1].description.ice_pwd,
788 local_transports[1].description.ice_pwd);
789}
790
791} // namespace webrtc