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