Rewrite WebRtcSession ICE tests as PeerConnection tests
Bug: webrtc:8222 Change-Id: Ie138712beaae964f08150b29ba5a8ed17ea0e956 Reviewed-on: https://webrtc-review.googlesource.com/4640 Commit-Queue: Steve Anton <steveanton@webrtc.org> Reviewed-by: Taylor Brandstetter <deadbeef@webrtc.org> Cr-Commit-Position: refs/heads/master@{#20296}
This commit is contained in:
parent
99c3fe55c7
commit
f1c6db1c02
@ -28,7 +28,7 @@ const int ICE_UFRAG_LENGTH = 4;
|
||||
const int ICE_PWD_LENGTH = 24;
|
||||
const size_t ICE_UFRAG_MIN_LENGTH = 4;
|
||||
const size_t ICE_PWD_MIN_LENGTH = 22;
|
||||
const size_t ICE_UFRAG_MAX_LENGTH = 255;
|
||||
const size_t ICE_UFRAG_MAX_LENGTH = 256;
|
||||
const size_t ICE_PWD_MAX_LENGTH = 256;
|
||||
|
||||
// This is media-specific, so might belong
|
||||
|
||||
@ -392,6 +392,7 @@ if (rtc_include_tests) {
|
||||
"mediaconstraintsinterface_unittest.cc",
|
||||
"mediastream_unittest.cc",
|
||||
"peerconnection_crypto_unittest.cc",
|
||||
"peerconnection_ice_unittest.cc",
|
||||
"peerconnection_integrationtest.cc",
|
||||
"peerconnection_rtp_unittest.cc",
|
||||
"peerconnectionendtoend_unittest.cc",
|
||||
|
||||
787
pc/peerconnection_ice_unittest.cc
Normal file
787
pc/peerconnection_ice_unittest.cc
Normal file
@ -0,0 +1,787 @@
|
||||
/*
|
||||
* Copyright 2017 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "p2p/base/fakeportallocator.h"
|
||||
#include "p2p/base/teststunserver.h"
|
||||
#include "p2p/client/basicportallocator.h"
|
||||
#include "pc/mediasession.h"
|
||||
#include "pc/peerconnectionwrapper.h"
|
||||
#include "pc/sdputils.h"
|
||||
#ifdef WEBRTC_ANDROID
|
||||
#include "pc/test/androidtestinitializer.h"
|
||||
#endif
|
||||
#include "pc/test/fakeaudiocapturemodule.h"
|
||||
#include "rtc_base/fakenetwork.h"
|
||||
#include "rtc_base/gunit.h"
|
||||
#include "rtc_base/ptr_util.h"
|
||||
#include "rtc_base/virtualsocketserver.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
|
||||
using RTCOfferAnswerOptions = PeerConnectionInterface::RTCOfferAnswerOptions;
|
||||
using rtc::SocketAddress;
|
||||
using ::testing::Values;
|
||||
|
||||
constexpr int kIceCandidatesTimeout = 10000;
|
||||
|
||||
class PeerConnectionWrapperForIceUnitTest : public PeerConnectionWrapper {
|
||||
public:
|
||||
using PeerConnectionWrapper::PeerConnectionWrapper;
|
||||
|
||||
// Adds a new ICE candidate to the first transport.
|
||||
bool AddIceCandidate(cricket::Candidate* candidate) {
|
||||
RTC_DCHECK(pc()->remote_description());
|
||||
const auto* desc = pc()->remote_description()->description();
|
||||
RTC_DCHECK(desc->contents().size() > 0);
|
||||
const auto& first_content = desc->contents()[0];
|
||||
candidate->set_transport_name(first_content.name);
|
||||
JsepIceCandidate jsep_candidate(first_content.name, 0, *candidate);
|
||||
return pc()->AddIceCandidate(&jsep_candidate);
|
||||
}
|
||||
|
||||
// Returns ICE candidates from the remote session description.
|
||||
std::vector<const IceCandidateInterface*>
|
||||
GetIceCandidatesFromRemoteDescription() {
|
||||
const SessionDescriptionInterface* sdesc = pc()->remote_description();
|
||||
RTC_DCHECK(sdesc);
|
||||
std::vector<const IceCandidateInterface*> candidates;
|
||||
for (size_t mline_index = 0; mline_index < sdesc->number_of_mediasections();
|
||||
mline_index++) {
|
||||
const auto* candidate_collection = sdesc->candidates(mline_index);
|
||||
for (size_t i = 0; i < candidate_collection->count(); i++) {
|
||||
candidates.push_back(candidate_collection->at(i));
|
||||
}
|
||||
}
|
||||
return candidates;
|
||||
}
|
||||
|
||||
rtc::FakeNetworkManager* network() { return network_; }
|
||||
|
||||
void set_network(rtc::FakeNetworkManager* network) { network_ = network; }
|
||||
|
||||
private:
|
||||
rtc::FakeNetworkManager* network_;
|
||||
};
|
||||
|
||||
class PeerConnectionIceUnitTest : public ::testing::Test {
|
||||
protected:
|
||||
typedef std::unique_ptr<PeerConnectionWrapperForIceUnitTest> WrapperPtr;
|
||||
|
||||
PeerConnectionIceUnitTest()
|
||||
: vss_(new rtc::VirtualSocketServer()), main_(vss_.get()) {
|
||||
#ifdef WEBRTC_ANDROID
|
||||
InitializeAndroidObjects();
|
||||
#endif
|
||||
pc_factory_ = CreatePeerConnectionFactory(
|
||||
rtc::Thread::Current(), rtc::Thread::Current(), rtc::Thread::Current(),
|
||||
FakeAudioCaptureModule::Create(), nullptr, nullptr);
|
||||
}
|
||||
|
||||
WrapperPtr CreatePeerConnection() {
|
||||
return CreatePeerConnection(RTCConfiguration());
|
||||
}
|
||||
|
||||
WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
|
||||
auto* fake_network = NewFakeNetwork();
|
||||
auto port_allocator =
|
||||
rtc::MakeUnique<cricket::BasicPortAllocator>(fake_network);
|
||||
port_allocator->set_flags(cricket::PORTALLOCATOR_DISABLE_TCP |
|
||||
cricket::PORTALLOCATOR_DISABLE_RELAY);
|
||||
port_allocator->set_step_delay(cricket::kMinimumStepDelay);
|
||||
auto observer = rtc::MakeUnique<MockPeerConnectionObserver>();
|
||||
auto pc = pc_factory_->CreatePeerConnection(
|
||||
config, std::move(port_allocator), nullptr, observer.get());
|
||||
if (!pc) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto wrapper = rtc::MakeUnique<PeerConnectionWrapperForIceUnitTest>(
|
||||
pc_factory_, pc, std::move(observer));
|
||||
wrapper->set_network(fake_network);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
// Accepts the same arguments as CreatePeerConnection and adds default audio
|
||||
// and video tracks.
|
||||
template <typename... Args>
|
||||
WrapperPtr CreatePeerConnectionWithAudioVideo(Args&&... args) {
|
||||
auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
|
||||
if (!wrapper) {
|
||||
return nullptr;
|
||||
}
|
||||
wrapper->AddAudioVideoStream("s", "a", "v");
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
cricket::Candidate CreateLocalUdpCandidate(
|
||||
const rtc::SocketAddress& address) {
|
||||
cricket::Candidate candidate;
|
||||
candidate.set_component(cricket::ICE_CANDIDATE_COMPONENT_DEFAULT);
|
||||
candidate.set_protocol(cricket::UDP_PROTOCOL_NAME);
|
||||
candidate.set_address(address);
|
||||
candidate.set_type(cricket::LOCAL_PORT_TYPE);
|
||||
return candidate;
|
||||
}
|
||||
|
||||
// Remove all ICE ufrag/pwd lines from the given session description.
|
||||
void RemoveIceUfragPwd(SessionDescriptionInterface* sdesc) {
|
||||
SetIceUfragPwd(sdesc, "", "");
|
||||
}
|
||||
|
||||
// Sets all ICE ufrag/pwds on the given session description.
|
||||
void SetIceUfragPwd(SessionDescriptionInterface* sdesc,
|
||||
const std::string& ufrag,
|
||||
const std::string& pwd) {
|
||||
auto* desc = sdesc->description();
|
||||
for (const auto& content : desc->contents()) {
|
||||
auto* transport_info = desc->GetTransportInfoByName(content.name);
|
||||
transport_info->description.ice_ufrag = ufrag;
|
||||
transport_info->description.ice_pwd = pwd;
|
||||
}
|
||||
}
|
||||
|
||||
cricket::TransportDescription* GetFirstTransportDescription(
|
||||
SessionDescriptionInterface* sdesc) {
|
||||
auto* desc = sdesc->description();
|
||||
RTC_DCHECK(desc->contents().size() > 0);
|
||||
auto* transport_info =
|
||||
desc->GetTransportInfoByName(desc->contents()[0].name);
|
||||
RTC_DCHECK(transport_info);
|
||||
return &transport_info->description;
|
||||
}
|
||||
|
||||
const cricket::TransportDescription* GetFirstTransportDescription(
|
||||
const SessionDescriptionInterface* sdesc) {
|
||||
auto* desc = sdesc->description();
|
||||
RTC_DCHECK(desc->contents().size() > 0);
|
||||
auto* transport_info =
|
||||
desc->GetTransportInfoByName(desc->contents()[0].name);
|
||||
RTC_DCHECK(transport_info);
|
||||
return &transport_info->description;
|
||||
}
|
||||
|
||||
bool AddCandidateToFirstTransport(cricket::Candidate* candidate,
|
||||
SessionDescriptionInterface* sdesc) {
|
||||
auto* desc = sdesc->description();
|
||||
RTC_DCHECK(desc->contents().size() > 0);
|
||||
const auto& first_content = desc->contents()[0];
|
||||
candidate->set_transport_name(first_content.name);
|
||||
JsepIceCandidate jsep_candidate(first_content.name, 0, *candidate);
|
||||
return sdesc->AddCandidate(&jsep_candidate);
|
||||
}
|
||||
|
||||
rtc::FakeNetworkManager* NewFakeNetwork() {
|
||||
// The PeerConnection's port allocator is tied to the PeerConnection's
|
||||
// lifetime and expects the underlying NetworkManager to outlive it. That
|
||||
// prevents us from having the PeerConnectionWrapper own the fake network.
|
||||
// Therefore, the test fixture will own all the fake networks even though
|
||||
// tests should access the fake network through the PeerConnectionWrapper.
|
||||
auto* fake_network = new rtc::FakeNetworkManager();
|
||||
fake_networks_.emplace_back(fake_network);
|
||||
return fake_network;
|
||||
}
|
||||
|
||||
std::unique_ptr<rtc::VirtualSocketServer> vss_;
|
||||
rtc::AutoSocketServerThread main_;
|
||||
rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
|
||||
std::vector<std::unique_ptr<rtc::FakeNetworkManager>> fake_networks_;
|
||||
};
|
||||
|
||||
::testing::AssertionResult AssertCandidatesEqual(const char* a_expr,
|
||||
const char* b_expr,
|
||||
const cricket::Candidate& a,
|
||||
const cricket::Candidate& b) {
|
||||
std::stringstream failure_info;
|
||||
if (a.component() != b.component()) {
|
||||
failure_info << "\ncomponent: " << a.component() << " != " << b.component();
|
||||
}
|
||||
if (a.protocol() != b.protocol()) {
|
||||
failure_info << "\nprotocol: " << a.protocol() << " != " << b.protocol();
|
||||
}
|
||||
if (a.address() != b.address()) {
|
||||
failure_info << "\naddress: " << a.address().ToString()
|
||||
<< " != " << b.address().ToString();
|
||||
}
|
||||
if (a.type() != b.type()) {
|
||||
failure_info << "\ntype: " << a.type() << " != " << b.type();
|
||||
}
|
||||
std::string failure_info_str = failure_info.str();
|
||||
if (failure_info_str.empty()) {
|
||||
return ::testing::AssertionSuccess();
|
||||
} else {
|
||||
return ::testing::AssertionFailure()
|
||||
<< a_expr << " and " << b_expr << " are not equal"
|
||||
<< failure_info_str;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionIceUnitTest, OfferContainsGatheredCandidates) {
|
||||
const SocketAddress kLocalAddress("1.1.1.1", 0);
|
||||
|
||||
auto caller = CreatePeerConnectionWithAudioVideo();
|
||||
caller->network()->AddInterface(kLocalAddress);
|
||||
|
||||
// Start ICE candidate gathering by setting the local offer.
|
||||
ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
|
||||
|
||||
EXPECT_TRUE_WAIT(caller->IsIceGatheringDone(), kIceCandidatesTimeout);
|
||||
|
||||
auto offer = caller->CreateOffer();
|
||||
EXPECT_LT(0u, caller->observer()->GetCandidatesByMline(0).size());
|
||||
EXPECT_EQ(caller->observer()->GetCandidatesByMline(0).size(),
|
||||
offer->candidates(0)->count());
|
||||
EXPECT_LT(0u, caller->observer()->GetCandidatesByMline(1).size());
|
||||
EXPECT_EQ(caller->observer()->GetCandidatesByMline(1).size(),
|
||||
offer->candidates(1)->count());
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionIceUnitTest, AnswerContainsGatheredCandidates) {
|
||||
const SocketAddress kCallerAddress("1.1.1.1", 0);
|
||||
|
||||
auto caller = CreatePeerConnectionWithAudioVideo();
|
||||
auto callee = CreatePeerConnectionWithAudioVideo();
|
||||
caller->network()->AddInterface(kCallerAddress);
|
||||
|
||||
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
||||
ASSERT_TRUE(callee->SetLocalDescription(callee->CreateAnswer()));
|
||||
|
||||
EXPECT_TRUE_WAIT(callee->IsIceGatheringDone(), kIceCandidatesTimeout);
|
||||
|
||||
auto answer = callee->CreateAnswer();
|
||||
EXPECT_LT(0u, caller->observer()->GetCandidatesByMline(0).size());
|
||||
EXPECT_EQ(callee->observer()->GetCandidatesByMline(0).size(),
|
||||
answer->candidates(0)->count());
|
||||
EXPECT_LT(0u, caller->observer()->GetCandidatesByMline(1).size());
|
||||
EXPECT_EQ(callee->observer()->GetCandidatesByMline(1).size(),
|
||||
answer->candidates(1)->count());
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionIceUnitTest,
|
||||
CanSetRemoteSessionDescriptionWithRemoteCandidates) {
|
||||
const SocketAddress kCallerAddress("1.1.1.1", 1111);
|
||||
|
||||
auto caller = CreatePeerConnectionWithAudioVideo();
|
||||
auto callee = CreatePeerConnectionWithAudioVideo();
|
||||
|
||||
auto offer = caller->CreateOfferAndSetAsLocal();
|
||||
cricket::Candidate candidate = CreateLocalUdpCandidate(kCallerAddress);
|
||||
AddCandidateToFirstTransport(&candidate, offer.get());
|
||||
|
||||
ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
|
||||
auto remote_candidates = callee->GetIceCandidatesFromRemoteDescription();
|
||||
ASSERT_EQ(1u, remote_candidates.size());
|
||||
EXPECT_PRED_FORMAT2(AssertCandidatesEqual, candidate,
|
||||
remote_candidates[0]->candidate());
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionIceUnitTest, SetLocalDescriptionFailsIfNoIceCredentials) {
|
||||
auto caller = CreatePeerConnectionWithAudioVideo();
|
||||
|
||||
auto offer = caller->CreateOffer();
|
||||
RemoveIceUfragPwd(offer.get());
|
||||
|
||||
EXPECT_FALSE(caller->SetLocalDescription(std::move(offer)));
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionIceUnitTest, SetRemoteDescriptionFailsIfNoIceCredentials) {
|
||||
auto caller = CreatePeerConnectionWithAudioVideo();
|
||||
auto callee = CreatePeerConnectionWithAudioVideo();
|
||||
|
||||
auto offer = caller->CreateOfferAndSetAsLocal();
|
||||
RemoveIceUfragPwd(offer.get());
|
||||
|
||||
EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
|
||||
}
|
||||
|
||||
// The following group tests that ICE candidates are not generated before
|
||||
// SetLocalDescription is called on a PeerConnection.
|
||||
|
||||
TEST_F(PeerConnectionIceUnitTest, NoIceCandidatesBeforeSetLocalDescription) {
|
||||
const SocketAddress kLocalAddress("1.1.1.1", 0);
|
||||
|
||||
auto caller = CreatePeerConnectionWithAudioVideo();
|
||||
caller->network()->AddInterface(kLocalAddress);
|
||||
|
||||
// Pump for 1 second and verify that no candidates are generated.
|
||||
rtc::Thread::Current()->ProcessMessages(1000);
|
||||
|
||||
EXPECT_EQ(0u, caller->observer()->candidates_.size());
|
||||
}
|
||||
TEST_F(PeerConnectionIceUnitTest,
|
||||
NoIceCandidatesBeforeAnswerSetAsLocalDescription) {
|
||||
const SocketAddress kCallerAddress("1.1.1.1", 1111);
|
||||
|
||||
auto caller = CreatePeerConnectionWithAudioVideo();
|
||||
auto callee = CreatePeerConnectionWithAudioVideo();
|
||||
caller->network()->AddInterface(kCallerAddress);
|
||||
|
||||
auto offer = caller->CreateOfferAndSetAsLocal();
|
||||
cricket::Candidate candidate = CreateLocalUdpCandidate(kCallerAddress);
|
||||
AddCandidateToFirstTransport(&candidate, offer.get());
|
||||
ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
|
||||
|
||||
// Pump for 1 second and verify that no candidates are generated.
|
||||
rtc::Thread::Current()->ProcessMessages(1000);
|
||||
|
||||
EXPECT_EQ(0u, callee->observer()->candidates_.size());
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionIceUnitTest,
|
||||
CannotAddCandidateWhenRemoteDescriptionNotSet) {
|
||||
const SocketAddress kCalleeAddress("1.1.1.1", 1111);
|
||||
|
||||
auto caller = CreatePeerConnectionWithAudioVideo();
|
||||
cricket::Candidate candidate = CreateLocalUdpCandidate(kCalleeAddress);
|
||||
JsepIceCandidate jsep_candidate(cricket::CN_AUDIO, 0, candidate);
|
||||
|
||||
EXPECT_FALSE(caller->pc()->AddIceCandidate(&jsep_candidate));
|
||||
|
||||
caller->CreateOfferAndSetAsLocal();
|
||||
|
||||
EXPECT_FALSE(caller->pc()->AddIceCandidate(&jsep_candidate));
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionIceUnitTest, DuplicateIceCandidateIgnoredWhenAdded) {
|
||||
const SocketAddress kCalleeAddress("1.1.1.1", 1111);
|
||||
|
||||
auto caller = CreatePeerConnectionWithAudioVideo();
|
||||
auto callee = CreatePeerConnectionWithAudioVideo();
|
||||
|
||||
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
||||
ASSERT_TRUE(
|
||||
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
|
||||
|
||||
cricket::Candidate candidate = CreateLocalUdpCandidate(kCalleeAddress);
|
||||
caller->AddIceCandidate(&candidate);
|
||||
EXPECT_TRUE(caller->AddIceCandidate(&candidate));
|
||||
EXPECT_EQ(1u, caller->GetIceCandidatesFromRemoteDescription().size());
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionIceUnitTest,
|
||||
AddRemoveCandidateWithEmptyTransportDoesNotCrash) {
|
||||
const SocketAddress kCalleeAddress("1.1.1.1", 1111);
|
||||
|
||||
auto caller = CreatePeerConnectionWithAudioVideo();
|
||||
auto callee = CreatePeerConnectionWithAudioVideo();
|
||||
|
||||
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
||||
ASSERT_TRUE(
|
||||
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
|
||||
|
||||
// |candidate.transport_name()| is empty.
|
||||
cricket::Candidate candidate = CreateLocalUdpCandidate(kCalleeAddress);
|
||||
JsepIceCandidate ice_candidate(cricket::CN_AUDIO, 0, candidate);
|
||||
EXPECT_TRUE(caller->pc()->AddIceCandidate(&ice_candidate));
|
||||
EXPECT_TRUE(caller->pc()->RemoveIceCandidates({candidate}));
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionIceUnitTest, RemoveCandidateRemovesFromRemoteDescription) {
|
||||
const SocketAddress kCalleeAddress("1.1.1.1", 1111);
|
||||
|
||||
auto caller = CreatePeerConnectionWithAudioVideo();
|
||||
auto callee = CreatePeerConnectionWithAudioVideo();
|
||||
|
||||
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
||||
ASSERT_TRUE(
|
||||
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
|
||||
|
||||
cricket::Candidate candidate = CreateLocalUdpCandidate(kCalleeAddress);
|
||||
ASSERT_TRUE(caller->AddIceCandidate(&candidate));
|
||||
EXPECT_TRUE(caller->pc()->RemoveIceCandidates({candidate}));
|
||||
EXPECT_EQ(0u, caller->GetIceCandidatesFromRemoteDescription().size());
|
||||
}
|
||||
|
||||
// Test that if a candidate is added via AddIceCandidate and via an updated
|
||||
// remote description, then both candidates appear in the stored remote
|
||||
// description.
|
||||
TEST_F(PeerConnectionIceUnitTest,
|
||||
CandidateInSubsequentOfferIsAddedToRemoteDescription) {
|
||||
const SocketAddress kCallerAddress1("1.1.1.1", 1111);
|
||||
const SocketAddress kCallerAddress2("2.2.2.2", 2222);
|
||||
|
||||
auto caller = CreatePeerConnectionWithAudioVideo();
|
||||
auto callee = CreatePeerConnectionWithAudioVideo();
|
||||
|
||||
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
||||
ASSERT_TRUE(
|
||||
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
|
||||
|
||||
// Add one candidate via |AddIceCandidate|.
|
||||
cricket::Candidate candidate1 = CreateLocalUdpCandidate(kCallerAddress1);
|
||||
ASSERT_TRUE(callee->AddIceCandidate(&candidate1));
|
||||
|
||||
// Add the second candidate via a reoffer.
|
||||
auto offer = caller->CreateOffer();
|
||||
cricket::Candidate candidate2 = CreateLocalUdpCandidate(kCallerAddress2);
|
||||
AddCandidateToFirstTransport(&candidate2, offer.get());
|
||||
|
||||
// Expect both candidates to appear in the callee's remote description.
|
||||
ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
|
||||
EXPECT_EQ(2u, callee->GetIceCandidatesFromRemoteDescription().size());
|
||||
}
|
||||
|
||||
// The follow test verifies that SetLocal/RemoteDescription fails when an offer
|
||||
// has either ICE ufrag/pwd too short or too long and succeeds otherwise.
|
||||
// The standard (https://tools.ietf.org/html/rfc5245#section-15.4) says that
|
||||
// pwd must be 22-256 characters and ufrag must be 4-256 characters.
|
||||
TEST_F(PeerConnectionIceUnitTest, VerifyUfragPwdLength) {
|
||||
auto caller = CreatePeerConnectionWithAudioVideo();
|
||||
auto callee = CreatePeerConnectionWithAudioVideo();
|
||||
|
||||
auto set_local_description_with_ufrag_pwd_length =
|
||||
[this, &caller](int ufrag_len, int pwd_len) {
|
||||
auto offer = caller->CreateOffer();
|
||||
SetIceUfragPwd(offer.get(), std::string(ufrag_len, 'x'),
|
||||
std::string(pwd_len, 'x'));
|
||||
return caller->SetLocalDescription(std::move(offer));
|
||||
};
|
||||
|
||||
auto set_remote_description_with_ufrag_pwd_length =
|
||||
[this, &caller, &callee](int ufrag_len, int pwd_len) {
|
||||
auto offer = caller->CreateOffer();
|
||||
SetIceUfragPwd(offer.get(), std::string(ufrag_len, 'x'),
|
||||
std::string(pwd_len, 'x'));
|
||||
return callee->SetRemoteDescription(std::move(offer));
|
||||
};
|
||||
|
||||
EXPECT_FALSE(set_local_description_with_ufrag_pwd_length(3, 22));
|
||||
EXPECT_FALSE(set_remote_description_with_ufrag_pwd_length(3, 22));
|
||||
EXPECT_FALSE(set_local_description_with_ufrag_pwd_length(257, 22));
|
||||
EXPECT_FALSE(set_remote_description_with_ufrag_pwd_length(257, 22));
|
||||
EXPECT_FALSE(set_local_description_with_ufrag_pwd_length(4, 21));
|
||||
EXPECT_FALSE(set_remote_description_with_ufrag_pwd_length(4, 21));
|
||||
EXPECT_FALSE(set_local_description_with_ufrag_pwd_length(4, 257));
|
||||
EXPECT_FALSE(set_remote_description_with_ufrag_pwd_length(4, 257));
|
||||
EXPECT_TRUE(set_local_description_with_ufrag_pwd_length(4, 22));
|
||||
EXPECT_TRUE(set_remote_description_with_ufrag_pwd_length(4, 22));
|
||||
EXPECT_TRUE(set_local_description_with_ufrag_pwd_length(256, 256));
|
||||
EXPECT_TRUE(set_remote_description_with_ufrag_pwd_length(256, 256));
|
||||
}
|
||||
|
||||
::testing::AssertionResult AssertIpInCandidates(
|
||||
const char* address_expr,
|
||||
const char* candidates_expr,
|
||||
const SocketAddress& address,
|
||||
const std::vector<IceCandidateInterface*> candidates) {
|
||||
std::stringstream candidate_hosts;
|
||||
for (const auto* candidate : candidates) {
|
||||
const auto& candidate_ip = candidate->candidate().address().ipaddr();
|
||||
if (candidate_ip == address.ipaddr()) {
|
||||
return ::testing::AssertionSuccess();
|
||||
}
|
||||
candidate_hosts << "\n" << candidate_ip;
|
||||
}
|
||||
return ::testing::AssertionFailure()
|
||||
<< address_expr << " (host " << address.HostAsURIString()
|
||||
<< ") not in " << candidates_expr
|
||||
<< " which have the following address hosts:" << candidate_hosts.str();
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionIceUnitTest, CandidatesGeneratedForEachLocalInterface) {
|
||||
const SocketAddress kLocalAddress1("1.1.1.1", 0);
|
||||
const SocketAddress kLocalAddress2("2.2.2.2", 0);
|
||||
|
||||
auto caller = CreatePeerConnectionWithAudioVideo();
|
||||
caller->network()->AddInterface(kLocalAddress1);
|
||||
caller->network()->AddInterface(kLocalAddress2);
|
||||
|
||||
caller->CreateOfferAndSetAsLocal();
|
||||
EXPECT_TRUE_WAIT(caller->IsIceGatheringDone(), kIceCandidatesTimeout);
|
||||
|
||||
auto candidates = caller->observer()->GetCandidatesByMline(0);
|
||||
EXPECT_PRED_FORMAT2(AssertIpInCandidates, kLocalAddress1, candidates);
|
||||
EXPECT_PRED_FORMAT2(AssertIpInCandidates, kLocalAddress2, candidates);
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionIceUnitTest,
|
||||
TrickledSingleCandidateAddedToRemoteDescription) {
|
||||
const SocketAddress kCallerAddress("1.1.1.1", 1111);
|
||||
|
||||
auto caller = CreatePeerConnectionWithAudioVideo();
|
||||
auto callee = CreatePeerConnectionWithAudioVideo();
|
||||
|
||||
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
||||
|
||||
cricket::Candidate candidate = CreateLocalUdpCandidate(kCallerAddress);
|
||||
callee->AddIceCandidate(&candidate);
|
||||
auto candidates = callee->GetIceCandidatesFromRemoteDescription();
|
||||
ASSERT_EQ(1u, candidates.size());
|
||||
EXPECT_PRED_FORMAT2(AssertCandidatesEqual, candidate,
|
||||
candidates[0]->candidate());
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionIceUnitTest,
|
||||
TwoTrickledCandidatesAddedToRemoteDescription) {
|
||||
const SocketAddress kCalleeAddress1("1.1.1.1", 1111);
|
||||
const SocketAddress kCalleeAddress2("2.2.2.2", 2222);
|
||||
|
||||
auto caller = CreatePeerConnectionWithAudioVideo();
|
||||
auto callee = CreatePeerConnectionWithAudioVideo();
|
||||
|
||||
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
||||
ASSERT_TRUE(
|
||||
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
|
||||
|
||||
cricket::Candidate candidate1 = CreateLocalUdpCandidate(kCalleeAddress1);
|
||||
caller->AddIceCandidate(&candidate1);
|
||||
|
||||
cricket::Candidate candidate2 = CreateLocalUdpCandidate(kCalleeAddress2);
|
||||
caller->AddIceCandidate(&candidate2);
|
||||
|
||||
auto candidates = caller->GetIceCandidatesFromRemoteDescription();
|
||||
ASSERT_EQ(2u, candidates.size());
|
||||
EXPECT_PRED_FORMAT2(AssertCandidatesEqual, candidate1,
|
||||
candidates[0]->candidate());
|
||||
EXPECT_PRED_FORMAT2(AssertCandidatesEqual, candidate2,
|
||||
candidates[1]->candidate());
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionIceUnitTest,
|
||||
LocalDescriptionUpdatedWhenContinualGathering) {
|
||||
const SocketAddress kLocalAddress("1.1.1.1", 0);
|
||||
|
||||
RTCConfiguration config;
|
||||
config.continual_gathering_policy =
|
||||
PeerConnectionInterface::GATHER_CONTINUALLY;
|
||||
auto caller = CreatePeerConnectionWithAudioVideo(config);
|
||||
caller->network()->AddInterface(kLocalAddress);
|
||||
|
||||
// Start ICE candidate gathering by setting the local offer.
|
||||
ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
|
||||
|
||||
// Since we're using continual gathering, we won't get "gathering done".
|
||||
EXPECT_TRUE_WAIT(
|
||||
caller->pc()->local_description()->candidates(0)->count() > 0,
|
||||
kIceCandidatesTimeout);
|
||||
}
|
||||
|
||||
// Test that when continual gathering is enabled, and a network interface goes
|
||||
// down, the candidate is signaled as removed and removed from the local
|
||||
// description.
|
||||
TEST_F(PeerConnectionIceUnitTest,
|
||||
LocalCandidatesRemovedWhenNetworkDownIfGatheringContinually) {
|
||||
const SocketAddress kLocalAddress("1.1.1.1", 0);
|
||||
|
||||
RTCConfiguration config;
|
||||
config.continual_gathering_policy =
|
||||
PeerConnectionInterface::GATHER_CONTINUALLY;
|
||||
auto caller = CreatePeerConnectionWithAudioVideo(config);
|
||||
caller->network()->AddInterface(kLocalAddress);
|
||||
|
||||
// Start ICE candidate gathering by setting the local offer.
|
||||
ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
|
||||
|
||||
EXPECT_TRUE_WAIT(
|
||||
caller->pc()->local_description()->candidates(0)->count() > 0,
|
||||
kIceCandidatesTimeout);
|
||||
|
||||
// Remove the only network interface, causing the PeerConnection to signal
|
||||
// the removal of all candidates derived from this interface.
|
||||
caller->network()->RemoveInterface(kLocalAddress);
|
||||
|
||||
EXPECT_EQ_WAIT(0u, caller->pc()->local_description()->candidates(0)->count(),
|
||||
kIceCandidatesTimeout);
|
||||
EXPECT_LT(0, caller->observer()->num_candidates_removed_);
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionIceUnitTest,
|
||||
LocalCandidatesNotRemovedWhenNetworkDownIfGatheringOnce) {
|
||||
const SocketAddress kLocalAddress("1.1.1.1", 0);
|
||||
|
||||
RTCConfiguration config;
|
||||
config.continual_gathering_policy = PeerConnectionInterface::GATHER_ONCE;
|
||||
auto caller = CreatePeerConnectionWithAudioVideo(config);
|
||||
caller->network()->AddInterface(kLocalAddress);
|
||||
|
||||
// Start ICE candidate gathering by setting the local offer.
|
||||
ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
|
||||
|
||||
EXPECT_TRUE_WAIT(caller->IsIceGatheringDone(), kIceCandidatesTimeout);
|
||||
|
||||
caller->network()->RemoveInterface(kLocalAddress);
|
||||
|
||||
// Verify that the local candidates are not removed;
|
||||
rtc::Thread::Current()->ProcessMessages(1000);
|
||||
EXPECT_EQ(0, caller->observer()->num_candidates_removed_);
|
||||
}
|
||||
|
||||
// The following group tests that when an offer includes a new ufrag or pwd
|
||||
// (indicating an ICE restart) the old candidates are removed and new candidates
|
||||
// added to the remote description.
|
||||
|
||||
TEST_F(PeerConnectionIceUnitTest, IceRestartOfferClearsExistingCandidate) {
|
||||
const SocketAddress kCallerAddress("1.1.1.1", 1111);
|
||||
|
||||
auto caller = CreatePeerConnectionWithAudioVideo();
|
||||
auto callee = CreatePeerConnectionWithAudioVideo();
|
||||
|
||||
auto offer = caller->CreateOffer();
|
||||
cricket::Candidate candidate = CreateLocalUdpCandidate(kCallerAddress);
|
||||
AddCandidateToFirstTransport(&candidate, offer.get());
|
||||
|
||||
ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
|
||||
|
||||
RTCOfferAnswerOptions options;
|
||||
options.ice_restart = true;
|
||||
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOffer(options)));
|
||||
|
||||
EXPECT_EQ(0u, callee->GetIceCandidatesFromRemoteDescription().size());
|
||||
}
|
||||
TEST_F(PeerConnectionIceUnitTest,
|
||||
IceRestartOfferCandidateReplacesExistingCandidate) {
|
||||
const SocketAddress kFirstCallerAddress("1.1.1.1", 1111);
|
||||
const SocketAddress kRestartedCallerAddress("2.2.2.2", 2222);
|
||||
|
||||
auto caller = CreatePeerConnectionWithAudioVideo();
|
||||
auto callee = CreatePeerConnectionWithAudioVideo();
|
||||
|
||||
auto offer = caller->CreateOffer();
|
||||
cricket::Candidate old_candidate =
|
||||
CreateLocalUdpCandidate(kFirstCallerAddress);
|
||||
AddCandidateToFirstTransport(&old_candidate, offer.get());
|
||||
|
||||
ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
|
||||
|
||||
RTCOfferAnswerOptions options;
|
||||
options.ice_restart = true;
|
||||
auto restart_offer = caller->CreateOffer(options);
|
||||
cricket::Candidate new_candidate =
|
||||
CreateLocalUdpCandidate(kRestartedCallerAddress);
|
||||
AddCandidateToFirstTransport(&new_candidate, restart_offer.get());
|
||||
|
||||
ASSERT_TRUE(callee->SetRemoteDescription(std::move(restart_offer)));
|
||||
|
||||
auto remote_candidates = callee->GetIceCandidatesFromRemoteDescription();
|
||||
ASSERT_EQ(1u, remote_candidates.size());
|
||||
EXPECT_PRED_FORMAT2(AssertCandidatesEqual, new_candidate,
|
||||
remote_candidates[0]->candidate());
|
||||
}
|
||||
|
||||
// Test that if there is not an ICE restart (i.e., nothing changes), then the
|
||||
// answer to a later offer should have the same ufrag/pwd as the first answer.
|
||||
TEST_F(PeerConnectionIceUnitTest,
|
||||
LaterAnswerHasSameIceCredentialsIfNoIceRestart) {
|
||||
auto caller = CreatePeerConnectionWithAudioVideo();
|
||||
auto callee = CreatePeerConnectionWithAudioVideo();
|
||||
|
||||
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
||||
ASSERT_TRUE(
|
||||
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
|
||||
|
||||
// Re-offer.
|
||||
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
||||
|
||||
auto answer = callee->CreateAnswer();
|
||||
auto* answer_transport_desc = GetFirstTransportDescription(answer.get());
|
||||
auto* local_transport_desc =
|
||||
GetFirstTransportDescription(callee->pc()->local_description());
|
||||
|
||||
EXPECT_EQ(answer_transport_desc->ice_ufrag, local_transport_desc->ice_ufrag);
|
||||
EXPECT_EQ(answer_transport_desc->ice_pwd, local_transport_desc->ice_pwd);
|
||||
}
|
||||
|
||||
// The following parameterized test verifies that if an offer is sent with a
|
||||
// modified ICE ufrag and/or ICE pwd, then the answer should identify that the
|
||||
// other side has initiated an ICE restart and generate a new ufrag and pwd.
|
||||
// RFC 5245 says: "If the offer contained a change in the a=ice-ufrag or
|
||||
// a=ice-pwd attributes compared to the previous SDP from the peer, it
|
||||
// indicates that ICE is restarting for this media stream."
|
||||
|
||||
class PeerConnectionIceUfragPwdAnswerUnitTest
|
||||
: public PeerConnectionIceUnitTest,
|
||||
public ::testing::WithParamInterface<std::pair<bool, bool>> {
|
||||
protected:
|
||||
PeerConnectionIceUfragPwdAnswerUnitTest() {
|
||||
offer_new_ufrag_ = GetParam().first;
|
||||
offer_new_pwd_ = GetParam().second;
|
||||
}
|
||||
|
||||
bool offer_new_ufrag_;
|
||||
bool offer_new_pwd_;
|
||||
};
|
||||
|
||||
TEST_P(PeerConnectionIceUfragPwdAnswerUnitTest, TestIncludedInAnswer) {
|
||||
auto caller = CreatePeerConnectionWithAudioVideo();
|
||||
auto callee = CreatePeerConnectionWithAudioVideo();
|
||||
|
||||
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
||||
ASSERT_TRUE(
|
||||
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
|
||||
|
||||
auto offer = caller->CreateOffer();
|
||||
auto* offer_transport_desc = GetFirstTransportDescription(offer.get());
|
||||
if (offer_new_ufrag_) {
|
||||
offer_transport_desc->ice_ufrag += "_new";
|
||||
}
|
||||
if (offer_new_pwd_) {
|
||||
offer_transport_desc->ice_pwd += "_new";
|
||||
}
|
||||
|
||||
ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
|
||||
|
||||
auto answer = callee->CreateAnswer();
|
||||
auto* answer_transport_desc = GetFirstTransportDescription(answer.get());
|
||||
auto* local_transport_desc =
|
||||
GetFirstTransportDescription(callee->pc()->local_description());
|
||||
|
||||
EXPECT_NE(answer_transport_desc->ice_ufrag, local_transport_desc->ice_ufrag);
|
||||
EXPECT_NE(answer_transport_desc->ice_pwd, local_transport_desc->ice_pwd);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
PeerConnectionIceUnitTest,
|
||||
PeerConnectionIceUfragPwdAnswerUnitTest,
|
||||
Values(std::make_pair(true, true), // Both changed.
|
||||
std::make_pair(true, false), // Only ufrag changed.
|
||||
std::make_pair(false, true))); // Only pwd changed.
|
||||
|
||||
// Test that if an ICE restart is offered on one media section, then the answer
|
||||
// will only change ICE ufrag/pwd for that section and keep the other sections
|
||||
// the same.
|
||||
// Note that this only works if we have disabled BUNDLE, otherwise all media
|
||||
// sections will share the same transport.
|
||||
TEST_F(PeerConnectionIceUnitTest,
|
||||
CreateAnswerHasNewUfragPwdForOnlyMediaSectionWhichRestarted) {
|
||||
auto caller = CreatePeerConnectionWithAudioVideo();
|
||||
auto callee = CreatePeerConnectionWithAudioVideo();
|
||||
|
||||
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
||||
ASSERT_TRUE(
|
||||
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
|
||||
|
||||
RTCOfferAnswerOptions disable_bundle_options;
|
||||
disable_bundle_options.use_rtp_mux = false;
|
||||
|
||||
auto offer = caller->CreateOffer(disable_bundle_options);
|
||||
|
||||
// Signal ICE restart on the first media section.
|
||||
auto* offer_transport_desc = GetFirstTransportDescription(offer.get());
|
||||
offer_transport_desc->ice_ufrag += "_new";
|
||||
offer_transport_desc->ice_pwd += "_new";
|
||||
|
||||
ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
|
||||
|
||||
auto answer = callee->CreateAnswer(disable_bundle_options);
|
||||
const auto& answer_transports = answer->description()->transport_infos();
|
||||
const auto& local_transports =
|
||||
callee->pc()->local_description()->description()->transport_infos();
|
||||
|
||||
EXPECT_NE(answer_transports[0].description.ice_ufrag,
|
||||
local_transports[0].description.ice_ufrag);
|
||||
EXPECT_NE(answer_transports[0].description.ice_pwd,
|
||||
local_transports[0].description.ice_pwd);
|
||||
EXPECT_EQ(answer_transports[1].description.ice_ufrag,
|
||||
local_transports[1].description.ice_ufrag);
|
||||
EXPECT_EQ(answer_transports[1].description.ice_pwd,
|
||||
local_transports[1].description.ice_pwd);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
@ -1578,7 +1578,7 @@ TEST_F(PeerConnectionInterfaceTest, RenegotiateAudioOnly) {
|
||||
TEST_F(PeerConnectionInterfaceTest, IceCandidates) {
|
||||
CreatePeerConnectionWithoutDtls();
|
||||
|
||||
EXPECT_FALSE(pc_->AddIceCandidate(observer_.last_candidate_.get()));
|
||||
EXPECT_FALSE(pc_->AddIceCandidate(observer_.last_candidate()));
|
||||
// SetRemoteDescription takes ownership of offer.
|
||||
std::unique_ptr<SessionDescriptionInterface> offer;
|
||||
AddVideoStream(kStreamLabel1);
|
||||
@ -1590,10 +1590,10 @@ TEST_F(PeerConnectionInterfaceTest, IceCandidates) {
|
||||
EXPECT_TRUE(DoCreateAnswer(&answer, nullptr));
|
||||
EXPECT_TRUE(DoSetLocalDescription(std::move(answer)));
|
||||
|
||||
EXPECT_TRUE_WAIT(observer_.last_candidate_.get() != NULL, kTimeout);
|
||||
EXPECT_TRUE_WAIT(observer_.last_candidate() != nullptr, kTimeout);
|
||||
EXPECT_TRUE_WAIT(observer_.ice_complete_, kTimeout);
|
||||
|
||||
EXPECT_TRUE(pc_->AddIceCandidate(observer_.last_candidate_.get()));
|
||||
EXPECT_TRUE(pc_->AddIceCandidate(observer_.last_candidate()));
|
||||
}
|
||||
|
||||
// Test that CreateOffer and CreateAnswer will fail if the track labels are
|
||||
|
||||
@ -166,4 +166,8 @@ void PeerConnectionWrapper::AddAudioVideoStream(
|
||||
observer()->renegotiation_needed_ = false;
|
||||
}
|
||||
|
||||
bool PeerConnectionWrapper::IsIceGatheringDone() {
|
||||
return observer()->ice_complete_;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -91,6 +91,9 @@ class PeerConnectionWrapper {
|
||||
const std::string& audio_track_label,
|
||||
const std::string& video_track_label);
|
||||
|
||||
// Returns true if ICE has finished gathering candidates.
|
||||
bool IsIceGatheringDone();
|
||||
|
||||
private:
|
||||
std::unique_ptr<SessionDescriptionInterface> CreateSdp(
|
||||
std::function<void(CreateSessionDescriptionObserver*)> fn);
|
||||
|
||||
@ -18,8 +18,10 @@
|
||||
#include <string>
|
||||
|
||||
#include "api/datachannelinterface.h"
|
||||
#include "api/jsepicecandidate.h"
|
||||
#include "pc/streamcollection.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/ptr_util.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
@ -79,22 +81,18 @@ class MockPeerConnectionObserver : public PeerConnectionObserver {
|
||||
ice_complete_ = new_state == PeerConnectionInterface::kIceGatheringComplete;
|
||||
callback_triggered_ = true;
|
||||
}
|
||||
void OnIceCandidate(const webrtc::IceCandidateInterface* candidate) override {
|
||||
void OnIceCandidate(const IceCandidateInterface* candidate) override {
|
||||
RTC_DCHECK(PeerConnectionInterface::kIceGatheringNew !=
|
||||
pc_->ice_gathering_state());
|
||||
|
||||
std::string sdp;
|
||||
bool success = candidate->ToString(&sdp);
|
||||
RTC_DCHECK(success);
|
||||
RTC_DCHECK(!sdp.empty());
|
||||
last_candidate_.reset(webrtc::CreateIceCandidate(
|
||||
candidate->sdp_mid(), candidate->sdp_mline_index(), sdp, NULL));
|
||||
RTC_DCHECK(last_candidate_);
|
||||
candidates_.push_back(rtc::MakeUnique<JsepIceCandidate>(
|
||||
candidate->sdp_mid(), candidate->sdp_mline_index(),
|
||||
candidate->candidate()));
|
||||
callback_triggered_ = true;
|
||||
}
|
||||
|
||||
void OnIceCandidatesRemoved(
|
||||
const std::vector<cricket::Candidate>& candidates) override {
|
||||
num_candidates_removed_++;
|
||||
callback_triggered_ = true;
|
||||
}
|
||||
|
||||
@ -137,9 +135,27 @@ class MockPeerConnectionObserver : public PeerConnectionObserver {
|
||||
return "";
|
||||
}
|
||||
|
||||
IceCandidateInterface* last_candidate() {
|
||||
if (candidates_.empty()) {
|
||||
return nullptr;
|
||||
} else {
|
||||
return candidates_.back().get();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<IceCandidateInterface*> GetCandidatesByMline(int mline_index) {
|
||||
std::vector<IceCandidateInterface*> candidates;
|
||||
for (const auto& candidate : candidates_) {
|
||||
if (candidate->sdp_mline_index() == mline_index) {
|
||||
candidates.push_back(candidate.get());
|
||||
}
|
||||
}
|
||||
return candidates;
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<PeerConnectionInterface> pc_;
|
||||
PeerConnectionInterface::SignalingState state_;
|
||||
std::unique_ptr<IceCandidateInterface> last_candidate_;
|
||||
std::vector<std::unique_ptr<IceCandidateInterface>> candidates_;
|
||||
rtc::scoped_refptr<DataChannelInterface> last_datachannel_;
|
||||
rtc::scoped_refptr<StreamCollection> remote_streams_;
|
||||
bool renegotiation_needed_ = false;
|
||||
@ -149,6 +165,7 @@ class MockPeerConnectionObserver : public PeerConnectionObserver {
|
||||
std::string last_added_track_label_;
|
||||
std::vector<AddTrackEvent> add_track_events_;
|
||||
std::vector<rtc::scoped_refptr<RtpReceiverInterface>> remove_track_events_;
|
||||
int num_candidates_removed_ = 0;
|
||||
|
||||
private:
|
||||
rtc::scoped_refptr<MediaStreamInterface> last_added_stream_;
|
||||
|
||||
@ -694,8 +694,13 @@ bool TransportController::RemoveRemoteCandidates_n(const Candidates& candidates,
|
||||
|
||||
std::map<std::string, Candidates> candidates_by_transport_name;
|
||||
for (const Candidate& cand : candidates) {
|
||||
RTC_DCHECK(!cand.transport_name().empty());
|
||||
candidates_by_transport_name[cand.transport_name()].push_back(cand);
|
||||
if (!cand.transport_name().empty()) {
|
||||
candidates_by_transport_name[cand.transport_name()].push_back(cand);
|
||||
} else {
|
||||
LOG(LS_ERROR) << "Not removing candidate because it does not have a "
|
||||
"transport name set: "
|
||||
<< cand.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
bool result = true;
|
||||
|
||||
@ -95,12 +95,6 @@ static const char kMediaContentName1[] = "video";
|
||||
static const int kDefaultTimeout = 10000; // 10 seconds.
|
||||
static const int kIceCandidatesTimeout = 10000;
|
||||
|
||||
static const char kTooLongIceUfragPwd[] =
|
||||
"IceUfragIceUfragIceUfragIceUfragIceUfragIceUfragIceUfragIceUfragIceUfrag"
|
||||
"IceUfragIceUfragIceUfragIceUfragIceUfragIceUfragIceUfragIceUfragIceUfrag"
|
||||
"IceUfragIceUfragIceUfragIceUfragIceUfragIceUfragIceUfragIceUfragIceUfrag"
|
||||
"IceUfragIceUfragIceUfragIceUfragIceUfragIceUfragIceUfragIceUfragIceUfrag";
|
||||
|
||||
static const char kSdpWithRtx[] =
|
||||
"v=0\r\n"
|
||||
"o=- 4104004319237231850 2 IN IP4 127.0.0.1\r\n"
|
||||
@ -784,31 +778,6 @@ class WebRtcSessionTest
|
||||
tdesc_factory_->set_secure(cricket::SEC_REQUIRED);
|
||||
}
|
||||
|
||||
bool IceUfragPwdEqual(const cricket::SessionDescription* desc1,
|
||||
const cricket::SessionDescription* desc2) {
|
||||
if (desc1->contents().size() != desc2->contents().size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const cricket::ContentInfos& contents = desc1->contents();
|
||||
cricket::ContentInfos::const_iterator it = contents.begin();
|
||||
|
||||
for (; it != contents.end(); ++it) {
|
||||
const cricket::TransportDescription* transport_desc1 =
|
||||
desc1->GetTransportDescriptionByName(it->name);
|
||||
const cricket::TransportDescription* transport_desc2 =
|
||||
desc2->GetTransportDescriptionByName(it->name);
|
||||
if (!transport_desc1 || !transport_desc2) {
|
||||
return false;
|
||||
}
|
||||
if (transport_desc1->ice_pwd != transport_desc2->ice_pwd ||
|
||||
transport_desc1->ice_ufrag != transport_desc2->ice_ufrag) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Compares ufrag/password only for the specified |media_type|.
|
||||
bool IceUfragPwdEqual(const cricket::SessionDescription* desc1,
|
||||
const cricket::SessionDescription* desc2,
|
||||
@ -833,42 +802,6 @@ class WebRtcSessionTest
|
||||
return true;
|
||||
}
|
||||
|
||||
void RemoveIceUfragPwdLines(const SessionDescriptionInterface* current_desc,
|
||||
std::string *sdp) {
|
||||
const cricket::SessionDescription* desc = current_desc->description();
|
||||
EXPECT_TRUE(current_desc->ToString(sdp));
|
||||
|
||||
const cricket::ContentInfos& contents = desc->contents();
|
||||
cricket::ContentInfos::const_iterator it = contents.begin();
|
||||
// Replace ufrag and pwd lines with empty strings.
|
||||
for (; it != contents.end(); ++it) {
|
||||
const cricket::TransportDescription* transport_desc =
|
||||
desc->GetTransportDescriptionByName(it->name);
|
||||
std::string ufrag_line = "a=ice-ufrag:" + transport_desc->ice_ufrag
|
||||
+ "\r\n";
|
||||
std::string pwd_line = "a=ice-pwd:" + transport_desc->ice_pwd
|
||||
+ "\r\n";
|
||||
rtc::replace_substrs(ufrag_line.c_str(), ufrag_line.length(),
|
||||
"", 0,
|
||||
sdp);
|
||||
rtc::replace_substrs(pwd_line.c_str(), pwd_line.length(),
|
||||
"", 0,
|
||||
sdp);
|
||||
}
|
||||
}
|
||||
|
||||
void SetIceUfragPwd(SessionDescriptionInterface* current_desc,
|
||||
const std::string& ufrag,
|
||||
const std::string& pwd) {
|
||||
cricket::SessionDescription* desc = current_desc->description();
|
||||
for (TransportInfo& transport_info : desc->transport_infos()) {
|
||||
cricket::TransportDescription& transport_desc =
|
||||
transport_info.description;
|
||||
transport_desc.ice_ufrag = ufrag;
|
||||
transport_desc.ice_pwd = pwd;
|
||||
}
|
||||
}
|
||||
|
||||
// Sets ufrag/pwd for specified |media_type|.
|
||||
void SetIceUfragPwd(SessionDescriptionInterface* current_desc,
|
||||
cricket::MediaType media_type,
|
||||
@ -1422,40 +1355,6 @@ TEST_F(WebRtcSessionTest, TestSessionCandidatesWithBundleRtcpMux) {
|
||||
TestSessionCandidatesWithBundleRtcpMux(true, true);
|
||||
}
|
||||
|
||||
TEST_F(WebRtcSessionTest, TestMultihomeCandidates) {
|
||||
AddInterface(rtc::SocketAddress(kClientAddrHost1, kClientAddrPort));
|
||||
AddInterface(rtc::SocketAddress(kClientAddrHost2, kClientAddrPort));
|
||||
Init();
|
||||
SendAudioVideoStream1();
|
||||
InitiateCall();
|
||||
EXPECT_TRUE_WAIT(observer_.oncandidatesready_, kIceCandidatesTimeout);
|
||||
EXPECT_EQ(8u, observer_.mline_0_candidates_.size());
|
||||
EXPECT_EQ(8u, observer_.mline_1_candidates_.size());
|
||||
}
|
||||
|
||||
TEST_F(WebRtcSessionTest, TestStunError) {
|
||||
rtc::ScopedFakeClock clock;
|
||||
|
||||
AddInterface(rtc::SocketAddress(kClientAddrHost1, kClientAddrPort));
|
||||
AddInterface(rtc::SocketAddress(kClientAddrHost2, kClientAddrPort));
|
||||
fss_->AddRule(false,
|
||||
rtc::FP_UDP,
|
||||
rtc::FD_ANY,
|
||||
rtc::SocketAddress(kClientAddrHost1, kClientAddrPort));
|
||||
Init();
|
||||
SendAudioVideoStream1();
|
||||
InitiateCall();
|
||||
// Since kClientAddrHost1 is blocked, not expecting stun candidates for it.
|
||||
EXPECT_TRUE_SIMULATED_WAIT(observer_.oncandidatesready_,
|
||||
cricket::STUN_TOTAL_TIMEOUT, clock);
|
||||
EXPECT_EQ(6u, observer_.mline_0_candidates_.size());
|
||||
EXPECT_EQ(6u, observer_.mline_1_candidates_.size());
|
||||
// Destroy session before scoped fake clock goes out of scope to avoid TSan
|
||||
// warning.
|
||||
session_->Close();
|
||||
session_.reset(nullptr);
|
||||
}
|
||||
|
||||
TEST_F(WebRtcSessionTest, SetSdpFailedOnInvalidSdp) {
|
||||
Init();
|
||||
SessionDescriptionInterface* offer = NULL;
|
||||
@ -1769,254 +1668,6 @@ TEST_F(WebRtcSessionTest, TestSetRemoteAnswerWithoutOffer) {
|
||||
"Called in wrong state: STATE_INIT", answer);
|
||||
}
|
||||
|
||||
// Tests that the remote candidates are added and removed successfully.
|
||||
TEST_F(WebRtcSessionTest, TestAddAndRemoveRemoteCandidates) {
|
||||
Init();
|
||||
SendAudioVideoStream1();
|
||||
|
||||
cricket::Candidate candidate(1, "udp", rtc::SocketAddress("1.1.1.1", 5000), 0,
|
||||
"", "", "local", 0, "");
|
||||
candidate.set_transport_name("audio");
|
||||
JsepIceCandidate ice_candidate1(kMediaContentName0, 0, candidate);
|
||||
|
||||
// Fail since we have not set a remote description.
|
||||
EXPECT_FALSE(session_->ProcessIceMessage(&ice_candidate1));
|
||||
|
||||
SessionDescriptionInterface* offer = CreateOffer();
|
||||
SetLocalDescriptionWithoutError(offer);
|
||||
|
||||
// Fail since we have not set a remote description.
|
||||
EXPECT_FALSE(session_->ProcessIceMessage(&ice_candidate1));
|
||||
|
||||
SessionDescriptionInterface* answer = CreateRemoteAnswer(
|
||||
session_->local_description());
|
||||
SetRemoteDescriptionWithoutError(answer);
|
||||
|
||||
EXPECT_TRUE(session_->ProcessIceMessage(&ice_candidate1));
|
||||
candidate.set_component(2);
|
||||
candidate.set_address(rtc::SocketAddress("2.2.2.2", 6000));
|
||||
JsepIceCandidate ice_candidate2(kMediaContentName0, 0, candidate);
|
||||
EXPECT_TRUE(session_->ProcessIceMessage(&ice_candidate2));
|
||||
|
||||
// Verifying the candidates are copied properly from internal vector.
|
||||
const SessionDescriptionInterface* remote_desc =
|
||||
session_->remote_description();
|
||||
ASSERT_TRUE(remote_desc != NULL);
|
||||
ASSERT_EQ(2u, remote_desc->number_of_mediasections());
|
||||
const IceCandidateCollection* candidates =
|
||||
remote_desc->candidates(kMediaContentIndex0);
|
||||
ASSERT_EQ(2u, candidates->count());
|
||||
EXPECT_EQ(kMediaContentIndex0, candidates->at(0)->sdp_mline_index());
|
||||
EXPECT_EQ(kMediaContentName0, candidates->at(0)->sdp_mid());
|
||||
EXPECT_EQ(1, candidates->at(0)->candidate().component());
|
||||
EXPECT_EQ(2, candidates->at(1)->candidate().component());
|
||||
|
||||
// |ice_candidate3| is identical to |ice_candidate2|. It can be added
|
||||
// successfully, but the total count of candidates will not increase.
|
||||
candidate.set_component(2);
|
||||
JsepIceCandidate ice_candidate3(kMediaContentName0, 0, candidate);
|
||||
EXPECT_TRUE(session_->ProcessIceMessage(&ice_candidate3));
|
||||
ASSERT_EQ(2u, candidates->count());
|
||||
|
||||
JsepIceCandidate bad_ice_candidate("bad content name", 99, candidate);
|
||||
EXPECT_FALSE(session_->ProcessIceMessage(&bad_ice_candidate));
|
||||
|
||||
// Remove candidate1 and candidate2
|
||||
std::vector<cricket::Candidate> remote_candidates;
|
||||
remote_candidates.push_back(ice_candidate1.candidate());
|
||||
remote_candidates.push_back(ice_candidate2.candidate());
|
||||
EXPECT_TRUE(session_->RemoveRemoteIceCandidates(remote_candidates));
|
||||
EXPECT_EQ(0u, candidates->count());
|
||||
}
|
||||
|
||||
// Tests that a remote candidate is added to the remote session description and
|
||||
// that it is retained if the remote session description is changed.
|
||||
TEST_F(WebRtcSessionTest, TestRemoteCandidatesAddedToSessionDescription) {
|
||||
Init();
|
||||
cricket::Candidate candidate1;
|
||||
candidate1.set_component(1);
|
||||
JsepIceCandidate ice_candidate1(kMediaContentName0, kMediaContentIndex0,
|
||||
candidate1);
|
||||
SendAudioVideoStream1();
|
||||
CreateAndSetRemoteOfferAndLocalAnswer();
|
||||
|
||||
EXPECT_TRUE(session_->ProcessIceMessage(&ice_candidate1));
|
||||
const SessionDescriptionInterface* remote_desc =
|
||||
session_->remote_description();
|
||||
ASSERT_TRUE(remote_desc != NULL);
|
||||
ASSERT_EQ(2u, remote_desc->number_of_mediasections());
|
||||
const IceCandidateCollection* candidates =
|
||||
remote_desc->candidates(kMediaContentIndex0);
|
||||
ASSERT_EQ(1u, candidates->count());
|
||||
EXPECT_EQ(kMediaContentIndex0, candidates->at(0)->sdp_mline_index());
|
||||
|
||||
// Update the RemoteSessionDescription with a new session description and
|
||||
// a candidate and check that the new remote session description contains both
|
||||
// candidates.
|
||||
SessionDescriptionInterface* offer = CreateRemoteOffer();
|
||||
cricket::Candidate candidate2;
|
||||
JsepIceCandidate ice_candidate2(kMediaContentName0, kMediaContentIndex0,
|
||||
candidate2);
|
||||
EXPECT_TRUE(offer->AddCandidate(&ice_candidate2));
|
||||
SetRemoteDescriptionWithoutError(offer);
|
||||
|
||||
remote_desc = session_->remote_description();
|
||||
ASSERT_TRUE(remote_desc != NULL);
|
||||
ASSERT_EQ(2u, remote_desc->number_of_mediasections());
|
||||
candidates = remote_desc->candidates(kMediaContentIndex0);
|
||||
ASSERT_EQ(2u, candidates->count());
|
||||
EXPECT_EQ(kMediaContentIndex0, candidates->at(0)->sdp_mline_index());
|
||||
// Username and password have be updated with the TransportInfo of the
|
||||
// SessionDescription, won't be equal to the original one.
|
||||
candidate2.set_username(candidates->at(0)->candidate().username());
|
||||
candidate2.set_password(candidates->at(0)->candidate().password());
|
||||
EXPECT_TRUE(candidate2.IsEquivalent(candidates->at(0)->candidate()));
|
||||
EXPECT_EQ(kMediaContentIndex0, candidates->at(1)->sdp_mline_index());
|
||||
// No need to verify the username and password.
|
||||
candidate1.set_username(candidates->at(1)->candidate().username());
|
||||
candidate1.set_password(candidates->at(1)->candidate().password());
|
||||
EXPECT_TRUE(candidate1.IsEquivalent(candidates->at(1)->candidate()));
|
||||
|
||||
// Test that the candidate is ignored if we can add the same candidate again.
|
||||
EXPECT_TRUE(session_->ProcessIceMessage(&ice_candidate2));
|
||||
}
|
||||
|
||||
// Test that local candidates are added to the local session description and
|
||||
// that they are retained if the local session description is changed. And if
|
||||
// continual gathering is enabled, they are removed from the local session
|
||||
// description when the network is down.
|
||||
TEST_F(WebRtcSessionTest,
|
||||
TestLocalCandidatesAddedAndRemovedIfGatherContinually) {
|
||||
AddInterface(rtc::SocketAddress(kClientAddrHost1, kClientAddrPort));
|
||||
Init();
|
||||
// Enable Continual Gathering.
|
||||
cricket::IceConfig config;
|
||||
config.continual_gathering_policy = cricket::GATHER_CONTINUALLY;
|
||||
session_->SetIceConfig(config);
|
||||
SendAudioVideoStream1();
|
||||
CreateAndSetRemoteOfferAndLocalAnswer();
|
||||
|
||||
const SessionDescriptionInterface* local_desc = session_->local_description();
|
||||
const IceCandidateCollection* candidates =
|
||||
local_desc->candidates(kMediaContentIndex0);
|
||||
ASSERT_TRUE(candidates != NULL);
|
||||
EXPECT_EQ(0u, candidates->count());
|
||||
|
||||
// Since we're using continual gathering, we won't get "gathering done".
|
||||
EXPECT_EQ_WAIT(2u, candidates->count(), kIceCandidatesTimeout);
|
||||
|
||||
local_desc = session_->local_description();
|
||||
candidates = local_desc->candidates(kMediaContentIndex0);
|
||||
ASSERT_TRUE(candidates != NULL);
|
||||
EXPECT_LT(0u, candidates->count());
|
||||
candidates = local_desc->candidates(1);
|
||||
ASSERT_TRUE(candidates != NULL);
|
||||
EXPECT_EQ(0u, candidates->count());
|
||||
|
||||
// Update the session descriptions.
|
||||
SendAudioVideoStream1();
|
||||
CreateAndSetRemoteOfferAndLocalAnswer();
|
||||
|
||||
local_desc = session_->local_description();
|
||||
candidates = local_desc->candidates(kMediaContentIndex0);
|
||||
ASSERT_TRUE(candidates != NULL);
|
||||
EXPECT_LT(0u, candidates->count());
|
||||
candidates = local_desc->candidates(1);
|
||||
ASSERT_TRUE(candidates != NULL);
|
||||
EXPECT_EQ(0u, candidates->count());
|
||||
|
||||
candidates = local_desc->candidates(kMediaContentIndex0);
|
||||
size_t num_local_candidates = candidates->count();
|
||||
// Bring down the network interface to trigger candidate removals.
|
||||
RemoveInterface(rtc::SocketAddress(kClientAddrHost1, kClientAddrPort));
|
||||
// Verify that all local candidates are removed.
|
||||
EXPECT_EQ(0, observer_.num_candidates_removed_);
|
||||
EXPECT_EQ_WAIT(num_local_candidates, observer_.num_candidates_removed_,
|
||||
kIceCandidatesTimeout);
|
||||
EXPECT_EQ_WAIT(0u, candidates->count(), kIceCandidatesTimeout);
|
||||
}
|
||||
|
||||
// Tests that if continual gathering is disabled, local candidates won't be
|
||||
// removed when the interface is turned down.
|
||||
TEST_F(WebRtcSessionTest, TestLocalCandidatesNotRemovedIfNotGatherContinually) {
|
||||
AddInterface(rtc::SocketAddress(kClientAddrHost1, kClientAddrPort));
|
||||
Init();
|
||||
SendAudioVideoStream1();
|
||||
CreateAndSetRemoteOfferAndLocalAnswer();
|
||||
|
||||
const SessionDescriptionInterface* local_desc = session_->local_description();
|
||||
const IceCandidateCollection* candidates =
|
||||
local_desc->candidates(kMediaContentIndex0);
|
||||
ASSERT_TRUE(candidates != NULL);
|
||||
EXPECT_TRUE_WAIT(observer_.oncandidatesready_, kIceCandidatesTimeout);
|
||||
|
||||
size_t num_local_candidates = candidates->count();
|
||||
EXPECT_LT(0u, num_local_candidates);
|
||||
// By default, Continual Gathering is disabled.
|
||||
// Bring down the network interface.
|
||||
RemoveInterface(rtc::SocketAddress(kClientAddrHost1, kClientAddrPort));
|
||||
// Verify that the local candidates are not removed.
|
||||
rtc::Thread::Current()->ProcessMessages(1000);
|
||||
EXPECT_EQ(0, observer_.num_candidates_removed_);
|
||||
EXPECT_EQ(num_local_candidates, candidates->count());
|
||||
}
|
||||
|
||||
// Test that we can set a remote session description with remote candidates.
|
||||
TEST_F(WebRtcSessionTest, TestSetRemoteSessionDescriptionWithCandidates) {
|
||||
Init();
|
||||
|
||||
cricket::Candidate candidate1;
|
||||
candidate1.set_component(1);
|
||||
JsepIceCandidate ice_candidate(kMediaContentName0, kMediaContentIndex0,
|
||||
candidate1);
|
||||
SendAudioVideoStream1();
|
||||
SessionDescriptionInterface* offer = CreateOffer();
|
||||
|
||||
EXPECT_TRUE(offer->AddCandidate(&ice_candidate));
|
||||
SetRemoteDescriptionWithoutError(offer);
|
||||
|
||||
const SessionDescriptionInterface* remote_desc =
|
||||
session_->remote_description();
|
||||
ASSERT_TRUE(remote_desc != NULL);
|
||||
ASSERT_EQ(2u, remote_desc->number_of_mediasections());
|
||||
const IceCandidateCollection* candidates =
|
||||
remote_desc->candidates(kMediaContentIndex0);
|
||||
ASSERT_EQ(1u, candidates->count());
|
||||
EXPECT_EQ(kMediaContentIndex0, candidates->at(0)->sdp_mline_index());
|
||||
|
||||
SessionDescriptionInterface* answer = CreateAnswer();
|
||||
SetLocalDescriptionWithoutError(answer);
|
||||
}
|
||||
|
||||
// Test that offers and answers contains ice candidates when Ice candidates have
|
||||
// been gathered.
|
||||
TEST_F(WebRtcSessionTest, TestSetLocalAndRemoteDescriptionWithCandidates) {
|
||||
AddInterface(rtc::SocketAddress(kClientAddrHost1, kClientAddrPort));
|
||||
Init();
|
||||
SendAudioVideoStream1();
|
||||
// Ice is started but candidates are not provided until SetLocalDescription
|
||||
// is called.
|
||||
EXPECT_EQ(0u, observer_.mline_0_candidates_.size());
|
||||
EXPECT_EQ(0u, observer_.mline_1_candidates_.size());
|
||||
CreateAndSetRemoteOfferAndLocalAnswer();
|
||||
// Wait until at least one local candidate has been collected.
|
||||
EXPECT_TRUE_WAIT(0u < observer_.mline_0_candidates_.size(),
|
||||
kIceCandidatesTimeout);
|
||||
|
||||
std::unique_ptr<SessionDescriptionInterface> local_offer(CreateOffer());
|
||||
|
||||
ASSERT_TRUE(local_offer);
|
||||
ASSERT_TRUE(local_offer->candidates(kMediaContentIndex0) != NULL);
|
||||
EXPECT_LT(0u, local_offer->candidates(kMediaContentIndex0)->count());
|
||||
|
||||
SessionDescriptionInterface* remote_offer(CreateRemoteOffer());
|
||||
SetRemoteDescriptionWithoutError(remote_offer);
|
||||
SessionDescriptionInterface* answer = CreateAnswer();
|
||||
ASSERT_TRUE(answer->candidates(kMediaContentIndex0) != NULL);
|
||||
EXPECT_LT(0u, answer->candidates(kMediaContentIndex0)->count());
|
||||
SetLocalDescriptionWithoutError(answer);
|
||||
}
|
||||
|
||||
// Verifies TransportProxy and media channels are created with content names
|
||||
// present in the SessionDescription.
|
||||
TEST_F(WebRtcSessionTest, TestChannelCreationsWithContentNames) {
|
||||
@ -2488,158 +2139,6 @@ TEST_F(WebRtcSessionTest, TestAVOfferWithVideoOnlyAnswer) {
|
||||
EXPECT_EQ(kVideoTrack2, video_channel_->send_streams()[0].id);
|
||||
}
|
||||
|
||||
// This test verifies that setLocalDescription fails if
|
||||
// no a=ice-ufrag and a=ice-pwd lines are present in the SDP.
|
||||
TEST_F(WebRtcSessionTest, TestSetLocalDescriptionWithoutIce) {
|
||||
Init();
|
||||
SendAudioVideoStream1();
|
||||
std::unique_ptr<SessionDescriptionInterface> offer(CreateOffer());
|
||||
|
||||
std::string sdp;
|
||||
RemoveIceUfragPwdLines(offer.get(), &sdp);
|
||||
SessionDescriptionInterface* modified_offer =
|
||||
CreateSessionDescription(JsepSessionDescription::kOffer, sdp, NULL);
|
||||
SetLocalDescriptionOfferExpectError(kSdpWithoutIceUfragPwd, modified_offer);
|
||||
}
|
||||
|
||||
// This test verifies that setRemoteDescription fails if
|
||||
// no a=ice-ufrag and a=ice-pwd lines are present in the SDP.
|
||||
TEST_F(WebRtcSessionTest, TestSetRemoteDescriptionWithoutIce) {
|
||||
Init();
|
||||
std::unique_ptr<SessionDescriptionInterface> offer(CreateRemoteOffer());
|
||||
std::string sdp;
|
||||
RemoveIceUfragPwdLines(offer.get(), &sdp);
|
||||
SessionDescriptionInterface* modified_offer =
|
||||
CreateSessionDescription(JsepSessionDescription::kOffer, sdp, NULL);
|
||||
SetRemoteDescriptionOfferExpectError(kSdpWithoutIceUfragPwd, modified_offer);
|
||||
}
|
||||
|
||||
// This test verifies that setLocalDescription fails if local offer has
|
||||
// too short ice ufrag and pwd strings.
|
||||
TEST_F(WebRtcSessionTest, TestSetLocalDescriptionInvalidIceCredentials) {
|
||||
Init();
|
||||
SendAudioVideoStream1();
|
||||
std::unique_ptr<SessionDescriptionInterface> offer(CreateOffer());
|
||||
// Modifying ice ufrag and pwd in local offer with strings smaller than the
|
||||
// recommended values of 4 and 22 bytes respectively.
|
||||
SetIceUfragPwd(offer.get(), "ice", "icepwd");
|
||||
std::string error;
|
||||
EXPECT_FALSE(session_->SetLocalDescription(offer.release(), &error));
|
||||
|
||||
// Test with string greater than 256.
|
||||
offer.reset(CreateOffer());
|
||||
SetIceUfragPwd(offer.get(), kTooLongIceUfragPwd, kTooLongIceUfragPwd);
|
||||
EXPECT_FALSE(session_->SetLocalDescription(offer.release(), &error));
|
||||
}
|
||||
|
||||
// This test verifies that setRemoteDescription fails if remote offer has
|
||||
// too short ice ufrag and pwd strings.
|
||||
TEST_F(WebRtcSessionTest, TestSetRemoteDescriptionInvalidIceCredentials) {
|
||||
Init();
|
||||
std::unique_ptr<SessionDescriptionInterface> offer(CreateRemoteOffer());
|
||||
// Modifying ice ufrag and pwd in remote offer with strings smaller than the
|
||||
// recommended values of 4 and 22 bytes respectively.
|
||||
SetIceUfragPwd(offer.get(), "ice", "icepwd");
|
||||
std::string error;
|
||||
EXPECT_FALSE(session_->SetRemoteDescription(offer.release(), &error));
|
||||
|
||||
offer.reset(CreateRemoteOffer());
|
||||
SetIceUfragPwd(offer.get(), kTooLongIceUfragPwd, kTooLongIceUfragPwd);
|
||||
EXPECT_FALSE(session_->SetRemoteDescription(offer.release(), &error));
|
||||
}
|
||||
|
||||
// Test that if the remote offer indicates the peer requested ICE restart (via
|
||||
// a new ufrag or pwd), the old ICE candidates are not copied, and vice versa.
|
||||
TEST_F(WebRtcSessionTest, TestSetRemoteOfferWithIceRestart) {
|
||||
Init();
|
||||
|
||||
// Create the first offer.
|
||||
std::unique_ptr<SessionDescriptionInterface> offer(CreateRemoteOffer());
|
||||
SetIceUfragPwd(offer.get(), "0123456789012345", "abcdefghijklmnopqrstuvwx");
|
||||
cricket::Candidate candidate1(1, "udp", rtc::SocketAddress("1.1.1.1", 5000),
|
||||
0, "", "", "relay", 0, "");
|
||||
JsepIceCandidate ice_candidate1(kMediaContentName0, kMediaContentIndex0,
|
||||
candidate1);
|
||||
EXPECT_TRUE(offer->AddCandidate(&ice_candidate1));
|
||||
SetRemoteDescriptionWithoutError(offer.release());
|
||||
EXPECT_EQ(1, session_->remote_description()->candidates(0)->count());
|
||||
|
||||
// The second offer has the same ufrag and pwd but different address.
|
||||
offer.reset(CreateRemoteOffer());
|
||||
SetIceUfragPwd(offer.get(), "0123456789012345", "abcdefghijklmnopqrstuvwx");
|
||||
candidate1.set_address(rtc::SocketAddress("1.1.1.1", 6000));
|
||||
JsepIceCandidate ice_candidate2(kMediaContentName0, kMediaContentIndex0,
|
||||
candidate1);
|
||||
EXPECT_TRUE(offer->AddCandidate(&ice_candidate2));
|
||||
SetRemoteDescriptionWithoutError(offer.release());
|
||||
EXPECT_EQ(2, session_->remote_description()->candidates(0)->count());
|
||||
|
||||
// The third offer has a different ufrag and different address.
|
||||
offer.reset(CreateRemoteOffer());
|
||||
SetIceUfragPwd(offer.get(), "0123456789012333", "abcdefghijklmnopqrstuvwx");
|
||||
candidate1.set_address(rtc::SocketAddress("1.1.1.1", 7000));
|
||||
JsepIceCandidate ice_candidate3(kMediaContentName0, kMediaContentIndex0,
|
||||
candidate1);
|
||||
EXPECT_TRUE(offer->AddCandidate(&ice_candidate3));
|
||||
SetRemoteDescriptionWithoutError(offer.release());
|
||||
EXPECT_EQ(1, session_->remote_description()->candidates(0)->count());
|
||||
|
||||
// The fourth offer has no candidate but a different ufrag/pwd.
|
||||
offer.reset(CreateRemoteOffer());
|
||||
SetIceUfragPwd(offer.get(), "0123456789012444", "abcdefghijklmnopqrstuvyz");
|
||||
SetRemoteDescriptionWithoutError(offer.release());
|
||||
EXPECT_EQ(0, session_->remote_description()->candidates(0)->count());
|
||||
}
|
||||
|
||||
// Test that if the remote answer indicates the peer requested ICE restart (via
|
||||
// a new ufrag or pwd), the old ICE candidates are not copied, and vice versa.
|
||||
TEST_F(WebRtcSessionTest, TestSetRemoteAnswerWithIceRestart) {
|
||||
Init();
|
||||
SessionDescriptionInterface* offer = CreateOffer();
|
||||
SetLocalDescriptionWithoutError(offer);
|
||||
|
||||
// Create the first answer.
|
||||
std::unique_ptr<JsepSessionDescription> answer(CreateRemoteAnswer(offer));
|
||||
answer->set_type(JsepSessionDescription::kPrAnswer);
|
||||
SetIceUfragPwd(answer.get(), "0123456789012345", "abcdefghijklmnopqrstuvwx");
|
||||
cricket::Candidate candidate1(1, "udp", rtc::SocketAddress("1.1.1.1", 5000),
|
||||
0, "", "", "relay", 0, "");
|
||||
JsepIceCandidate ice_candidate1(kMediaContentName0, kMediaContentIndex0,
|
||||
candidate1);
|
||||
EXPECT_TRUE(answer->AddCandidate(&ice_candidate1));
|
||||
SetRemoteDescriptionWithoutError(answer.release());
|
||||
EXPECT_EQ(1, session_->remote_description()->candidates(0)->count());
|
||||
|
||||
// The second answer has the same ufrag and pwd but different address.
|
||||
answer.reset(CreateRemoteAnswer(offer));
|
||||
answer->set_type(JsepSessionDescription::kPrAnswer);
|
||||
SetIceUfragPwd(answer.get(), "0123456789012345", "abcdefghijklmnopqrstuvwx");
|
||||
candidate1.set_address(rtc::SocketAddress("1.1.1.1", 6000));
|
||||
JsepIceCandidate ice_candidate2(kMediaContentName0, kMediaContentIndex0,
|
||||
candidate1);
|
||||
EXPECT_TRUE(answer->AddCandidate(&ice_candidate2));
|
||||
SetRemoteDescriptionWithoutError(answer.release());
|
||||
EXPECT_EQ(2, session_->remote_description()->candidates(0)->count());
|
||||
|
||||
// The third answer has a different ufrag and different address.
|
||||
answer.reset(CreateRemoteAnswer(offer));
|
||||
answer->set_type(JsepSessionDescription::kPrAnswer);
|
||||
SetIceUfragPwd(answer.get(), "0123456789012333", "abcdefghijklmnopqrstuvwx");
|
||||
candidate1.set_address(rtc::SocketAddress("1.1.1.1", 7000));
|
||||
JsepIceCandidate ice_candidate3(kMediaContentName0, kMediaContentIndex0,
|
||||
candidate1);
|
||||
EXPECT_TRUE(answer->AddCandidate(&ice_candidate3));
|
||||
SetRemoteDescriptionWithoutError(answer.release());
|
||||
EXPECT_EQ(1, session_->remote_description()->candidates(0)->count());
|
||||
|
||||
// The fourth answer has no candidate but a different ufrag/pwd.
|
||||
answer.reset(CreateRemoteAnswer(offer));
|
||||
answer->set_type(JsepSessionDescription::kPrAnswer);
|
||||
SetIceUfragPwd(answer.get(), "0123456789012444", "abcdefghijklmnopqrstuvyz");
|
||||
SetRemoteDescriptionWithoutError(answer.release());
|
||||
EXPECT_EQ(0, session_->remote_description()->candidates(0)->count());
|
||||
}
|
||||
|
||||
// Test that candidates sent to the "video" transport do not get pushed down to
|
||||
// the "audio" transport channel when bundling.
|
||||
TEST_F(WebRtcSessionTest, TestIgnoreCandidatesForUnusedTransportWhenBundling) {
|
||||
@ -3198,183 +2697,6 @@ TEST_F(WebRtcSessionTest, TestIncorrectMLinesInLocalAnswer) {
|
||||
SetLocalDescriptionWithoutError(answer);
|
||||
}
|
||||
|
||||
// This test verifies that WebRtcSession does not start candidate allocation
|
||||
// before SetLocalDescription is called.
|
||||
TEST_F(WebRtcSessionTest, TestIceStartAfterSetLocalDescriptionOnly) {
|
||||
Init();
|
||||
SendAudioVideoStream1();
|
||||
SessionDescriptionInterface* offer = CreateRemoteOffer();
|
||||
cricket::Candidate candidate;
|
||||
candidate.set_component(1);
|
||||
JsepIceCandidate ice_candidate(kMediaContentName0, kMediaContentIndex0,
|
||||
candidate);
|
||||
EXPECT_TRUE(offer->AddCandidate(&ice_candidate));
|
||||
cricket::Candidate candidate1;
|
||||
candidate1.set_component(1);
|
||||
JsepIceCandidate ice_candidate1(kMediaContentName1, kMediaContentIndex1,
|
||||
candidate1);
|
||||
EXPECT_TRUE(offer->AddCandidate(&ice_candidate1));
|
||||
SetRemoteDescriptionWithoutError(offer);
|
||||
ASSERT_TRUE(session_->voice_rtp_transport_channel() != NULL);
|
||||
ASSERT_TRUE(session_->video_rtp_transport_channel() != NULL);
|
||||
|
||||
// Pump for 1 second and verify that no candidates are generated.
|
||||
rtc::Thread::Current()->ProcessMessages(1000);
|
||||
EXPECT_TRUE(observer_.mline_0_candidates_.empty());
|
||||
EXPECT_TRUE(observer_.mline_1_candidates_.empty());
|
||||
|
||||
SessionDescriptionInterface* answer = CreateAnswer();
|
||||
SetLocalDescriptionWithoutError(answer);
|
||||
EXPECT_TRUE_WAIT(observer_.oncandidatesready_, kIceCandidatesTimeout);
|
||||
}
|
||||
|
||||
// This test verifies that an answer contains new ufrag and password if an offer
|
||||
// with new ufrag and password is received.
|
||||
TEST_F(WebRtcSessionTest, TestCreateAnswerWithNewUfragAndPassword) {
|
||||
Init();
|
||||
cricket::MediaSessionOptions options;
|
||||
GetOptionsForRemoteOffer(&options);
|
||||
std::unique_ptr<JsepSessionDescription> offer(CreateRemoteOffer(options));
|
||||
SetRemoteDescriptionWithoutError(offer.release());
|
||||
|
||||
SendAudioVideoStream1();
|
||||
std::unique_ptr<SessionDescriptionInterface> answer(CreateAnswer());
|
||||
SetLocalDescriptionWithoutError(answer.release());
|
||||
|
||||
// Receive an offer with new ufrag and password.
|
||||
for (size_t i = 0; i < options.media_description_options.size(); ++i) {
|
||||
options.media_description_options[i].transport_options.ice_restart = true;
|
||||
}
|
||||
|
||||
std::unique_ptr<JsepSessionDescription> updated_offer1(
|
||||
CreateRemoteOffer(options, session_->remote_description()));
|
||||
SetRemoteDescriptionWithoutError(updated_offer1.release());
|
||||
|
||||
std::unique_ptr<SessionDescriptionInterface> updated_answer1(CreateAnswer());
|
||||
|
||||
EXPECT_FALSE(IceUfragPwdEqual(updated_answer1->description(),
|
||||
session_->local_description()->description()));
|
||||
|
||||
// Even a second answer (created before the description is set) should have
|
||||
// a new ufrag/password.
|
||||
std::unique_ptr<SessionDescriptionInterface> updated_answer2(CreateAnswer());
|
||||
|
||||
EXPECT_FALSE(IceUfragPwdEqual(updated_answer2->description(),
|
||||
session_->local_description()->description()));
|
||||
|
||||
SetLocalDescriptionWithoutError(updated_answer2.release());
|
||||
}
|
||||
|
||||
// This test verifies that an answer contains new ufrag and password if an offer
|
||||
// that changes either the ufrag or password (but not both) is received.
|
||||
// RFC 5245 says: "If the offer contained a change in the a=ice-ufrag or
|
||||
// a=ice-pwd attributes compared to the previous SDP from the peer, it
|
||||
// indicates that ICE is restarting for this media stream."
|
||||
TEST_F(WebRtcSessionTest, TestOfferChangingOnlyUfragOrPassword) {
|
||||
Init();
|
||||
cricket::MediaSessionOptions options;
|
||||
GetOptionsForRemoteOffer(&options);
|
||||
// Create an offer with audio and video.
|
||||
std::unique_ptr<JsepSessionDescription> offer(CreateRemoteOffer(options));
|
||||
SetIceUfragPwd(offer.get(), "original_ufrag", "original_password12345");
|
||||
SetRemoteDescriptionWithoutError(offer.release());
|
||||
|
||||
SendAudioVideoStream1();
|
||||
std::unique_ptr<SessionDescriptionInterface> answer(CreateAnswer());
|
||||
SetLocalDescriptionWithoutError(answer.release());
|
||||
|
||||
// Receive an offer with a new ufrag but stale password.
|
||||
std::unique_ptr<JsepSessionDescription> ufrag_changed_offer(
|
||||
CreateRemoteOffer(options, session_->remote_description()));
|
||||
SetIceUfragPwd(ufrag_changed_offer.get(), "modified_ufrag",
|
||||
"original_password12345");
|
||||
SetRemoteDescriptionWithoutError(ufrag_changed_offer.release());
|
||||
|
||||
std::unique_ptr<SessionDescriptionInterface> updated_answer1(CreateAnswer());
|
||||
EXPECT_FALSE(IceUfragPwdEqual(updated_answer1->description(),
|
||||
session_->local_description()->description()));
|
||||
SetLocalDescriptionWithoutError(updated_answer1.release());
|
||||
|
||||
// Receive an offer with a new password but stale ufrag.
|
||||
std::unique_ptr<JsepSessionDescription> password_changed_offer(
|
||||
CreateRemoteOffer(options, session_->remote_description()));
|
||||
SetIceUfragPwd(password_changed_offer.get(), "modified_ufrag",
|
||||
"modified_password12345");
|
||||
SetRemoteDescriptionWithoutError(password_changed_offer.release());
|
||||
|
||||
std::unique_ptr<SessionDescriptionInterface> updated_answer2(CreateAnswer());
|
||||
EXPECT_FALSE(IceUfragPwdEqual(updated_answer2->description(),
|
||||
session_->local_description()->description()));
|
||||
SetLocalDescriptionWithoutError(updated_answer2.release());
|
||||
}
|
||||
|
||||
// This test verifies that an answer contains old ufrag and password if an offer
|
||||
// with old ufrag and password is received.
|
||||
TEST_F(WebRtcSessionTest, TestCreateAnswerWithOldUfragAndPassword) {
|
||||
Init();
|
||||
cricket::MediaSessionOptions options;
|
||||
GetOptionsForRemoteOffer(&options);
|
||||
std::unique_ptr<JsepSessionDescription> offer(CreateRemoteOffer(options));
|
||||
SetRemoteDescriptionWithoutError(offer.release());
|
||||
|
||||
SendAudioVideoStream1();
|
||||
std::unique_ptr<SessionDescriptionInterface> answer(CreateAnswer());
|
||||
SetLocalDescriptionWithoutError(answer.release());
|
||||
|
||||
// Receive an offer without changed ufrag or password.
|
||||
std::unique_ptr<JsepSessionDescription> updated_offer2(
|
||||
CreateRemoteOffer(options, session_->remote_description()));
|
||||
SetRemoteDescriptionWithoutError(updated_offer2.release());
|
||||
|
||||
std::unique_ptr<SessionDescriptionInterface> updated_answer2(CreateAnswer());
|
||||
|
||||
EXPECT_TRUE(IceUfragPwdEqual(updated_answer2->description(),
|
||||
session_->local_description()->description()));
|
||||
|
||||
SetLocalDescriptionWithoutError(updated_answer2.release());
|
||||
}
|
||||
|
||||
// This test verifies that if an offer does an ICE restart on some, but not all
|
||||
// media sections, the answer will change the ufrag/password in the correct
|
||||
// media sections.
|
||||
TEST_F(WebRtcSessionTest, TestCreateAnswerWithNewAndOldUfragAndPassword) {
|
||||
Init();
|
||||
cricket::MediaSessionOptions options;
|
||||
GetOptionsForRemoteOffer(&options);
|
||||
options.bundle_enabled = false;
|
||||
std::unique_ptr<JsepSessionDescription> offer(CreateRemoteOffer(options));
|
||||
|
||||
SetIceUfragPwd(offer.get(), cricket::MEDIA_TYPE_AUDIO, "aaaa",
|
||||
"aaaaaaaaaaaaaaaaaaaaaa");
|
||||
SetIceUfragPwd(offer.get(), cricket::MEDIA_TYPE_VIDEO, "bbbb",
|
||||
"bbbbbbbbbbbbbbbbbbbbbb");
|
||||
SetRemoteDescriptionWithoutError(offer.release());
|
||||
|
||||
SendAudioVideoStream1();
|
||||
std::unique_ptr<SessionDescriptionInterface> answer(CreateAnswer());
|
||||
SetLocalDescriptionWithoutError(answer.release());
|
||||
|
||||
// Receive an offer with new ufrag and password, but only for the video media
|
||||
// section.
|
||||
std::unique_ptr<JsepSessionDescription> updated_offer(
|
||||
CreateRemoteOffer(options, session_->remote_description()));
|
||||
SetIceUfragPwd(updated_offer.get(), cricket::MEDIA_TYPE_VIDEO, "cccc",
|
||||
"cccccccccccccccccccccc");
|
||||
SetRemoteDescriptionWithoutError(updated_offer.release());
|
||||
|
||||
std::unique_ptr<SessionDescriptionInterface> updated_answer(CreateAnswer());
|
||||
|
||||
EXPECT_TRUE(IceUfragPwdEqual(updated_answer->description(),
|
||||
session_->local_description()->description(),
|
||||
cricket::MEDIA_TYPE_AUDIO));
|
||||
|
||||
EXPECT_FALSE(IceUfragPwdEqual(updated_answer->description(),
|
||||
session_->local_description()->description(),
|
||||
cricket::MEDIA_TYPE_VIDEO));
|
||||
|
||||
SetLocalDescriptionWithoutError(updated_answer.release());
|
||||
}
|
||||
|
||||
TEST_F(WebRtcSessionTest, TestSessionContentError) {
|
||||
Init();
|
||||
SendAudioVideoStream1();
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
# Tests that are failing when run under memcheck.
|
||||
# https://code.google.com/p/webrtc/issues/detail?id=4387
|
||||
DtmfSenderTest.*
|
||||
PeerConnectionEndToEndTest.*
|
||||
PeerConnectionCryptoUnitTest.*
|
||||
PeerConnectionEndToEndTest.*
|
||||
PeerConnectionIceUnitTest.*
|
||||
PeerConnectionIntegrationTest.*
|
||||
PeerConnectionInterfaceTest.*
|
||||
PeerConnectionRtpTest.*
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user