From da6c095b3083e517b13d5307595df2e956e9e0e1 Mon Sep 17 00:00:00 2001 From: Steve Anton Date: Mon, 23 Oct 2017 11:41:54 -0700 Subject: [PATCH] Rewrite WebRtcSession data channel tests as PeerConnection tests Bug: webrtc:8222 Change-Id: I1382a0727b04dfd33e79992841d885f640b3a032 Reviewed-on: https://webrtc-review.googlesource.com/8281 Commit-Queue: Steve Anton Reviewed-by: Taylor Brandstetter Cr-Commit-Position: refs/heads/master@{#20398} --- pc/BUILD.gn | 2 + pc/peerconnection.cc | 8 +- pc/peerconnection_datachannel_unittest.cc | 301 ++++++++++++++++++++++ pc/peerconnection_integrationtest.cc | 25 +- pc/peerconnectionfactory.cc | 10 + pc/peerconnectionfactory.h | 5 + pc/test/fakesctptransport.h | 64 +++++ pc/webrtcsession_unittest.cc | 226 +--------------- 8 files changed, 408 insertions(+), 233 deletions(-) create mode 100644 pc/peerconnection_datachannel_unittest.cc create mode 100644 pc/test/fakesctptransport.h diff --git a/pc/BUILD.gn b/pc/BUILD.gn index 3e552c9bc7..e98ccc3cc0 100644 --- a/pc/BUILD.gn +++ b/pc/BUILD.gn @@ -324,6 +324,7 @@ if (rtc_include_tests) { "test/fakedatachannelprovider.h", "test/fakeperiodicvideocapturer.h", "test/fakertccertificategenerator.h", + "test/fakesctptransport.h", "test/faketransportcontroller.h", "test/fakevideotrackrenderer.h", "test/fakevideotracksource.h", @@ -393,6 +394,7 @@ if (rtc_include_tests) { "mediastream_unittest.cc", "peerconnection_bundle_unittest.cc", "peerconnection_crypto_unittest.cc", + "peerconnection_datachannel_unittest.cc", "peerconnection_ice_unittest.cc", "peerconnection_integrationtest.cc", "peerconnection_media_unittest.cc", diff --git a/pc/peerconnection.cc b/pc/peerconnection.cc index ac552ef054..b96818f48b 100644 --- a/pc/peerconnection.cc +++ b/pc/peerconnection.cc @@ -482,13 +482,7 @@ bool PeerConnection::Initialize( factory_->CreateTransportController( port_allocator_.get(), configuration.redetermine_role_on_ice_restart)), -#ifdef HAVE_SCTP - std::unique_ptr( - new cricket::SctpTransportFactory(network_thread())) -#else - nullptr -#endif - )); + factory_->CreateSctpTransportInternalFactory())); session_ = owned_session_.get(); stats_.reset(new StatsCollector(this)); diff --git a/pc/peerconnection_datachannel_unittest.cc b/pc/peerconnection_datachannel_unittest.cc new file mode 100644 index 0000000000..918544cc27 --- /dev/null +++ b/pc/peerconnection_datachannel_unittest.cc @@ -0,0 +1,301 @@ +/* + * 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 + +#include "api/peerconnectionproxy.h" +#include "media/base/fakemediaengine.h" +#include "pc/mediasession.h" +#include "pc/peerconnection.h" +#include "pc/peerconnectionfactory.h" +#include "pc/peerconnectionwrapper.h" +#ifdef WEBRTC_ANDROID +#include "pc/test/androidtestinitializer.h" +#endif +#include "pc/test/fakesctptransport.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 ::testing::Values; + +class PeerConnectionFactoryForDataChannelTest + : public rtc::RefCountedObject { + public: + PeerConnectionFactoryForDataChannelTest() + : rtc::RefCountedObject( + rtc::Thread::Current(), + rtc::Thread::Current(), + rtc::Thread::Current(), + rtc::MakeUnique(), + CreateCallFactory(), + nullptr) {} + + std::unique_ptr + CreateSctpTransportInternalFactory() { + auto factory = rtc::MakeUnique(); + last_fake_sctp_transport_factory_ = factory.get(); + return factory; + } + + FakeSctpTransportFactory* last_fake_sctp_transport_factory_ = nullptr; +}; + +class PeerConnectionWrapperForDataChannelTest : public PeerConnectionWrapper { + public: + using PeerConnectionWrapper::PeerConnectionWrapper; + + FakeSctpTransportFactory* sctp_transport_factory() { + return sctp_transport_factory_; + } + + void set_sctp_transport_factory( + FakeSctpTransportFactory* sctp_transport_factory) { + sctp_transport_factory_ = sctp_transport_factory; + } + + rtc::Optional sctp_content_name() { + return GetInternalPeerConnection()->sctp_content_name(); + } + + rtc::Optional sctp_transport_name() { + return GetInternalPeerConnection()->sctp_transport_name(); + } + + PeerConnection* GetInternalPeerConnection() { + auto* pci = reinterpret_cast< + PeerConnectionProxyWithInternal*>(pc()); + return reinterpret_cast(pci->internal()); + } + + private: + FakeSctpTransportFactory* sctp_transport_factory_ = nullptr; +}; + +class PeerConnectionDataChannelTest : public ::testing::Test { + protected: + typedef std::unique_ptr WrapperPtr; + + PeerConnectionDataChannelTest() + : vss_(new rtc::VirtualSocketServer()), main_(vss_.get()) { +#ifdef WEBRTC_ANDROID + InitializeAndroidObjects(); +#endif + } + + WrapperPtr CreatePeerConnection() { + return CreatePeerConnection(RTCConfiguration()); + } + + WrapperPtr CreatePeerConnection(const RTCConfiguration& config) { + return CreatePeerConnection(config, + PeerConnectionFactoryInterface::Options()); + } + + WrapperPtr CreatePeerConnection( + const RTCConfiguration& config, + const PeerConnectionFactoryInterface::Options factory_options) { + rtc::scoped_refptr pc_factory( + new PeerConnectionFactoryForDataChannelTest()); + pc_factory->SetOptions(factory_options); + RTC_CHECK(pc_factory->Initialize()); + auto observer = rtc::MakeUnique(); + auto pc = pc_factory->CreatePeerConnection(config, nullptr, nullptr, + observer.get()); + if (!pc) { + return nullptr; + } + + auto wrapper = rtc::MakeUnique( + pc_factory, pc, std::move(observer)); + RTC_DCHECK(pc_factory->last_fake_sctp_transport_factory_); + wrapper->set_sctp_transport_factory( + pc_factory->last_fake_sctp_transport_factory_); + return wrapper; + } + + // Accepts the same arguments as CreatePeerConnection and adds a default data + // channel. + template + WrapperPtr CreatePeerConnectionWithDataChannel(Args&&... args) { + auto wrapper = CreatePeerConnection(std::forward(args)...); + if (!wrapper) { + return nullptr; + } + EXPECT_TRUE(wrapper->pc()->CreateDataChannel("dc", nullptr)); + return wrapper; + } + + // Changes the SCTP data channel port on the given session description. + void ChangeSctpPortOnDescription(cricket::SessionDescription* desc, + int port) { + cricket::DataCodec sctp_codec(cricket::kGoogleSctpDataCodecPlType, + cricket::kGoogleSctpDataCodecName); + sctp_codec.SetParam(cricket::kCodecParamPort, port); + + auto* data_content = cricket::GetFirstDataContent(desc); + RTC_DCHECK(data_content); + auto* data_desc = static_cast( + data_content->description); + data_desc->set_codecs({sctp_codec}); + } + + std::unique_ptr vss_; + rtc::AutoSocketServerThread main_; +}; + +TEST_F(PeerConnectionDataChannelTest, + NoSctpTransportCreatedIfRtpDataChannelEnabled) { + RTCConfiguration config; + config.enable_rtp_data_channel = true; + auto caller = CreatePeerConnectionWithDataChannel(config); + + ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer())); + EXPECT_FALSE(caller->sctp_transport_factory()->last_fake_sctp_transport()); +} + +TEST_F(PeerConnectionDataChannelTest, + RtpDataChannelCreatedEvenIfSctpAvailable) { + RTCConfiguration config; + config.enable_rtp_data_channel = true; + PeerConnectionFactoryInterface::Options options; + options.disable_sctp_data_channels = false; + auto caller = CreatePeerConnectionWithDataChannel(config, options); + + ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer())); + EXPECT_FALSE(caller->sctp_transport_factory()->last_fake_sctp_transport()); +} + +// Test that sctp_content_name/sctp_transport_name (used for stats) are correct +// before and after BUNDLE is negotiated. +TEST_F(PeerConnectionDataChannelTest, SctpContentAndTransportNameSetCorrectly) { + auto caller = CreatePeerConnection(); + auto callee = CreatePeerConnection(); + + // Initially these fields should be empty. + EXPECT_FALSE(caller->sctp_content_name()); + EXPECT_FALSE(caller->sctp_transport_name()); + + // Create offer with audio/video/data. + // Default bundle policy is "balanced", so data should be using its own + // transport. + caller->AddAudioTrack("a"); + caller->AddVideoTrack("v"); + caller->pc()->CreateDataChannel("dc", nullptr); + ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal())); + + ASSERT_TRUE(caller->sctp_content_name()); + EXPECT_EQ(cricket::CN_DATA, *caller->sctp_content_name()); + ASSERT_TRUE(caller->sctp_transport_name()); + EXPECT_EQ(cricket::CN_DATA, *caller->sctp_transport_name()); + + // Create answer that finishes BUNDLE negotiation, which means everything + // should be bundled on the first transport (audio). + RTCOfferAnswerOptions options; + options.use_rtp_mux = true; + ASSERT_TRUE( + caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal())); + + ASSERT_TRUE(caller->sctp_content_name()); + EXPECT_EQ(cricket::CN_DATA, *caller->sctp_content_name()); + ASSERT_TRUE(caller->sctp_transport_name()); + EXPECT_EQ(cricket::CN_AUDIO, *caller->sctp_transport_name()); +} + +TEST_F(PeerConnectionDataChannelTest, + CreateOfferWithNoDataChannelsGivesNoDataSection) { + auto caller = CreatePeerConnection(); + auto offer = caller->CreateOffer(); + + EXPECT_FALSE(offer->description()->GetContentByName(cricket::CN_DATA)); + EXPECT_FALSE(offer->description()->GetTransportInfoByName(cricket::CN_DATA)); +} + +TEST_F(PeerConnectionDataChannelTest, + CreateAnswerWithRemoteSctpDataChannelIncludesDataSection) { + auto caller = CreatePeerConnectionWithDataChannel(); + auto callee = CreatePeerConnection(); + + ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal())); + + auto answer = callee->CreateAnswer(); + ASSERT_TRUE(answer); + auto* data_content = + answer->description()->GetContentByName(cricket::CN_DATA); + ASSERT_TRUE(data_content); + EXPECT_FALSE(data_content->rejected); + EXPECT_TRUE(answer->description()->GetTransportInfoByName(cricket::CN_DATA)); +} + +TEST_F(PeerConnectionDataChannelTest, + CreateDataChannelWithDtlsDisabledSucceeds) { + RTCConfiguration config; + config.enable_dtls_srtp.emplace(false); + auto caller = CreatePeerConnection(); + + EXPECT_TRUE(caller->pc()->CreateDataChannel("dc", nullptr)); +} + +TEST_F(PeerConnectionDataChannelTest, CreateDataChannelWithSctpDisabledFails) { + PeerConnectionFactoryInterface::Options options; + options.disable_sctp_data_channels = true; + auto caller = CreatePeerConnection(RTCConfiguration(), options); + + EXPECT_FALSE(caller->pc()->CreateDataChannel("dc", nullptr)); +} + +// Test that if a callee has SCTP disabled and receives an offer with an SCTP +// data channel, the data section is rejected and no SCTP transport is created +// on the callee. +TEST_F(PeerConnectionDataChannelTest, + DataSectionRejectedIfCalleeHasSctpDisabled) { + auto caller = CreatePeerConnectionWithDataChannel(); + PeerConnectionFactoryInterface::Options options; + options.disable_sctp_data_channels = true; + auto callee = CreatePeerConnection(RTCConfiguration(), options); + + ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal())); + + EXPECT_FALSE(callee->sctp_transport_factory()->last_fake_sctp_transport()); + + auto answer = callee->CreateAnswer(); + auto* data_content = + answer->description()->GetContentByName(cricket::CN_DATA); + ASSERT_TRUE(data_content); + EXPECT_TRUE(data_content->rejected); +} + +TEST_F(PeerConnectionDataChannelTest, SctpPortPropagatedFromSdpToTransport) { + constexpr int kNewSendPort = 9998; + constexpr int kNewRecvPort = 7775; + + auto caller = CreatePeerConnectionWithDataChannel(); + auto callee = CreatePeerConnectionWithDataChannel(); + + auto offer = caller->CreateOffer(); + ChangeSctpPortOnDescription(offer->description(), kNewSendPort); + ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer))); + + auto answer = callee->CreateAnswer(); + ChangeSctpPortOnDescription(answer->description(), kNewRecvPort); + ASSERT_TRUE(callee->SetLocalDescription(std::move(answer))); + + auto* callee_transport = + callee->sctp_transport_factory()->last_fake_sctp_transport(); + ASSERT_TRUE(callee_transport); + EXPECT_EQ(kNewSendPort, callee_transport->remote_port()); + EXPECT_EQ(kNewRecvPort, callee_transport->local_port()); +} + +} // namespace webrtc diff --git a/pc/peerconnection_integrationtest.cc b/pc/peerconnection_integrationtest.cc index d38433c18e..0d6c87f7b0 100644 --- a/pc/peerconnection_integrationtest.cc +++ b/pc/peerconnection_integrationtest.cc @@ -364,7 +364,12 @@ class PeerConnectionWrapper : public webrtc::PeerConnectionObserver, void CreateDataChannel() { CreateDataChannel(nullptr); } void CreateDataChannel(const webrtc::DataChannelInit* init) { - data_channel_ = pc()->CreateDataChannel(kDataChannelLabel, init); + CreateDataChannel(kDataChannelLabel, init); + } + + void CreateDataChannel(const std::string& label, + const webrtc::DataChannelInit* init) { + data_channel_ = pc()->CreateDataChannel(label, init); ASSERT_TRUE(data_channel_.get() != nullptr); data_observer_.reset(new MockDataChannelObserver(data_channel_)); } @@ -2594,6 +2599,24 @@ TEST_F(PeerConnectionIntegrationTest, CalleeClosesSctpDataChannel) { EXPECT_TRUE_WAIT(!callee()->data_observer()->IsOpen(), kDefaultTimeout); } +TEST_F(PeerConnectionIntegrationTest, SctpDataChannelConfigSentToOtherSide) { + ASSERT_TRUE(CreatePeerConnectionWrappers()); + ConnectFakeSignaling(); + webrtc::DataChannelInit init; + init.id = 53; + init.maxRetransmits = 52; + caller()->CreateDataChannel("data-channel", &init); + caller()->AddAudioVideoMediaStream(); + callee()->AddAudioVideoMediaStream(); + caller()->CreateAndSetAndSignalOffer(); + ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); + ASSERT_TRUE(callee()->data_channel()); + EXPECT_EQ(init.id, callee()->data_channel()->id()); + EXPECT_EQ("data-channel", callee()->data_channel()->label()); + EXPECT_EQ(init.maxRetransmits, callee()->data_channel()->maxRetransmits()); + EXPECT_FALSE(callee()->data_channel()->negotiated()); +} + // Test usrsctp's ability to process unordered data stream, where data actually // arrives out of order using simulated delays. Previously there have been some // bugs in this area. diff --git a/pc/peerconnectionfactory.cc b/pc/peerconnectionfactory.cc index 20edaa6af1..dc84d76d01 100644 --- a/pc/peerconnectionfactory.cc +++ b/pc/peerconnectionfactory.cc @@ -20,6 +20,7 @@ #include "api/turncustomizer.h" #include "api/videosourceproxy.h" #include "logging/rtc_event_log/rtc_event_log.h" +#include "media/sctp/sctptransport.h" #include "rtc_base/bind.h" #include "rtc_base/checks.h" #include "rtc_base/ptr_util.h" @@ -297,6 +298,15 @@ cricket::TransportController* PeerConnectionFactory::CreateTransportController( redetermine_role_on_ice_restart, options_.crypto_options); } +std::unique_ptr +PeerConnectionFactory::CreateSctpTransportInternalFactory() { +#ifdef HAVE_SCTP + return rtc::MakeUnique(network_thread()); +#else + return nullptr; +#endif +} + cricket::ChannelManager* PeerConnectionFactory::channel_manager() { return channel_manager_.get(); } diff --git a/pc/peerconnectionfactory.h b/pc/peerconnectionfactory.h index 7d71a69bd6..f5faeccc2c 100644 --- a/pc/peerconnectionfactory.h +++ b/pc/peerconnectionfactory.h @@ -16,6 +16,7 @@ #include "api/mediastreaminterface.h" #include "api/peerconnectioninterface.h" +#include "media/sctp/sctptransportinternal.h" #include "pc/channelmanager.h" #include "rtc_base/rtccertificategenerator.h" #include "rtc_base/scoped_ref_ptr.h" @@ -89,6 +90,10 @@ class PeerConnectionFactory : public PeerConnectionFactoryInterface { virtual cricket::TransportController* CreateTransportController( cricket::PortAllocator* port_allocator, bool redetermine_role_on_ice_restart); + + virtual std::unique_ptr + CreateSctpTransportInternalFactory(); + virtual cricket::ChannelManager* channel_manager(); virtual rtc::Thread* signaling_thread(); virtual rtc::Thread* worker_thread(); diff --git a/pc/test/fakesctptransport.h b/pc/test/fakesctptransport.h new file mode 100644 index 0000000000..17143ed66c --- /dev/null +++ b/pc/test/fakesctptransport.h @@ -0,0 +1,64 @@ +/* + * 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. + */ + +#ifndef PC_TEST_FAKESCTPTRANSPORT_H_ +#define PC_TEST_FAKESCTPTRANSPORT_H_ + +#include + +#include "media/sctp/sctptransportinternal.h" + +// Used for tests in this file to verify that PeerConnection responds to signals +// from the SctpTransport correctly, and calls Start with the correct +// local/remote ports. +class FakeSctpTransport : public cricket::SctpTransportInternal { + public: + void SetTransportChannel(rtc::PacketTransportInternal* channel) override {} + bool Start(int local_port, int remote_port) override { + local_port_.emplace(local_port); + remote_port_.emplace(remote_port); + return true; + } + bool OpenStream(int sid) override { return true; } + bool ResetStream(int sid) override { return true; } + bool SendData(const cricket::SendDataParams& params, + const rtc::CopyOnWriteBuffer& payload, + cricket::SendDataResult* result = nullptr) override { + return true; + } + bool ReadyToSendData() override { return true; } + void set_debug_name_for_testing(const char* debug_name) override {} + + int local_port() const { return *local_port_; } + int remote_port() const { return *remote_port_; } + + private: + rtc::Optional local_port_; + rtc::Optional remote_port_; +}; + +class FakeSctpTransportFactory : public cricket::SctpTransportInternalFactory { + public: + std::unique_ptr CreateSctpTransport( + rtc::PacketTransportInternal*) override { + last_fake_sctp_transport_ = new FakeSctpTransport(); + return std::unique_ptr( + last_fake_sctp_transport_); + } + + FakeSctpTransport* last_fake_sctp_transport() { + return last_fake_sctp_transport_; + } + + private: + FakeSctpTransport* last_fake_sctp_transport_ = nullptr; +}; + +#endif // PC_TEST_FAKESCTPTRANSPORT_H_ diff --git a/pc/webrtcsession_unittest.cc b/pc/webrtcsession_unittest.cc index a6fda21d27..ab83fb9fd8 100644 --- a/pc/webrtcsession_unittest.cc +++ b/pc/webrtcsession_unittest.cc @@ -31,6 +31,7 @@ #include "pc/peerconnection.h" #include "pc/sctputils.h" #include "pc/test/fakertccertificategenerator.h" +#include "pc/test/fakesctptransport.h" #include "pc/videotrack.h" #include "pc/webrtcsession.h" #include "pc/webrtcsessiondescriptionfactory.h" @@ -87,7 +88,6 @@ static const int kMediaContentIndex0 = 0; // Media index of candidates belonging to the second media content. static const int kMediaContentIndex1 = 1; -static const int kDefaultTimeout = 10000; // 10 seconds. static const int kIceCandidatesTimeout = 10000; static const char kStream1[] = "stream1"; @@ -161,52 +161,6 @@ class MockIceObserver : public webrtc::IceObserver { size_t num_candidates_removed_ = 0; }; -// Used for tests in this file to verify that WebRtcSession responds to signals -// from the SctpTransport correctly, and calls Start with the correct -// local/remote ports. -class FakeSctpTransport : public cricket::SctpTransportInternal { - public: - void SetTransportChannel(rtc::PacketTransportInternal* channel) override {} - bool Start(int local_port, int remote_port) override { - local_port_ = local_port; - remote_port_ = remote_port; - return true; - } - bool OpenStream(int sid) override { return true; } - bool ResetStream(int sid) override { return true; } - bool SendData(const cricket::SendDataParams& params, - const rtc::CopyOnWriteBuffer& payload, - cricket::SendDataResult* result = nullptr) override { - return true; - } - bool ReadyToSendData() override { return true; } - void set_debug_name_for_testing(const char* debug_name) override {} - - int local_port() const { return local_port_; } - int remote_port() const { return remote_port_; } - - private: - int local_port_ = -1; - int remote_port_ = -1; -}; - -class FakeSctpTransportFactory : public cricket::SctpTransportInternalFactory { - public: - std::unique_ptr CreateSctpTransport( - rtc::PacketTransportInternal*) override { - last_fake_sctp_transport_ = new FakeSctpTransport(); - return std::unique_ptr( - last_fake_sctp_transport_); - } - - FakeSctpTransport* last_fake_sctp_transport() { - return last_fake_sctp_transport_; - } - - private: - FakeSctpTransport* last_fake_sctp_transport_ = nullptr; -}; - class WebRtcSessionForTest : public webrtc::WebRtcSession { public: WebRtcSessionForTest( @@ -1077,184 +1031,6 @@ TEST_P(WebRtcSessionTest, TestCreateAnswerWithDifferentSslRoles) { SetLocalDescriptionWithoutError(answer); } -TEST_F(WebRtcSessionTest, TestRtpDataChannel) { - configuration_.enable_rtp_data_channel = true; - Init(); - SetLocalDescriptionWithDataChannel(); - ASSERT_TRUE(data_engine_); - EXPECT_NE(nullptr, data_engine_->GetChannel(0)); -} - -TEST_P(WebRtcSessionTest, TestRtpDataChannelConstraintTakesPrecedence) { - configuration_.enable_rtp_data_channel = true; - options_.disable_sctp_data_channels = false; - - InitWithDtls(GetParam()); - - SetLocalDescriptionWithDataChannel(); - EXPECT_NE(nullptr, data_engine_->GetChannel(0)); -} - -// Test that sctp_content_name/sctp_transport_name (used for stats) are correct -// before and after BUNDLE is negotiated. -TEST_P(WebRtcSessionTest, SctpContentAndTransportName) { - SetFactoryDtlsSrtp(); - InitWithDtls(GetParam()); - - // Initially these fields should be empty. - EXPECT_FALSE(session_->sctp_content_name()); - EXPECT_FALSE(session_->sctp_transport_name()); - - // Create offer with audio/video/data. - // Default bundle policy is "balanced", so data should be using its own - // transport. - SendAudioVideoStream1(); - CreateDataChannel(); - InitiateCall(); - ASSERT_TRUE(session_->sctp_content_name()); - ASSERT_TRUE(session_->sctp_transport_name()); - EXPECT_EQ("data", *session_->sctp_content_name()); - EXPECT_EQ("data", *session_->sctp_transport_name()); - - // Create answer that finishes BUNDLE negotiation, which means everything - // should be bundled on the first transport (audio). - cricket::MediaSessionOptions answer_options; - answer_options.bundle_enabled = true; - answer_options.data_channel_type = cricket::DCT_SCTP; - GetOptionsForAnswer(&answer_options); - SetRemoteDescriptionWithoutError(CreateRemoteAnswer( - session_->local_description(), answer_options, cricket::SEC_DISABLED)); - ASSERT_TRUE(session_->sctp_content_name()); - ASSERT_TRUE(session_->sctp_transport_name()); - EXPECT_EQ("data", *session_->sctp_content_name()); - EXPECT_EQ("audio", *session_->sctp_transport_name()); -} - -TEST_P(WebRtcSessionTest, TestCreateOfferWithSctpEnabledWithoutStreams) { - InitWithDtls(GetParam()); - - std::unique_ptr offer(CreateOffer()); - EXPECT_TRUE(offer->description()->GetContentByName("data") == NULL); - EXPECT_TRUE(offer->description()->GetTransportInfoByName("data") == NULL); -} - -TEST_P(WebRtcSessionTest, TestCreateAnswerWithSctpInOfferAndNoStreams) { - SetFactoryDtlsSrtp(); - InitWithDtls(GetParam()); - - // Create remote offer with SCTP. - cricket::MediaSessionOptions options; - options.data_channel_type = cricket::DCT_SCTP; - GetOptionsForRemoteOffer(&options); - JsepSessionDescription* offer = - CreateRemoteOffer(options, cricket::SEC_DISABLED); - SetRemoteDescriptionWithoutError(offer); - - // Verifies the answer contains SCTP. - std::unique_ptr answer(CreateAnswer()); - EXPECT_TRUE(answer != NULL); - EXPECT_TRUE(answer->description()->GetContentByName("data") != NULL); - EXPECT_TRUE(answer->description()->GetTransportInfoByName("data") != NULL); -} - -// Test that if DTLS is disabled, we don't end up with an SctpTransport -// created (or an RtpDataChannel). -TEST_P(WebRtcSessionTest, TestSctpDataChannelWithoutDtls) { - configuration_.enable_dtls_srtp = rtc::Optional(false); - InitWithDtls(GetParam()); - - SetLocalDescriptionWithDataChannel(); - EXPECT_EQ(nullptr, data_engine_->GetChannel(0)); - EXPECT_EQ(nullptr, fake_sctp_transport_factory_->last_fake_sctp_transport()); -} - -// Test that if DTLS is enabled, we end up with an SctpTransport created -// (and not an RtpDataChannel). -TEST_P(WebRtcSessionTest, TestSctpDataChannelWithDtls) { - InitWithDtls(GetParam()); - - SetLocalDescriptionWithDataChannel(); - EXPECT_EQ(nullptr, data_engine_->GetChannel(0)); - EXPECT_NE(nullptr, fake_sctp_transport_factory_->last_fake_sctp_transport()); -} - -// Test that if SCTP is disabled, we don't end up with an SctpTransport -// created (or an RtpDataChannel). -TEST_P(WebRtcSessionTest, TestDisableSctpDataChannels) { - options_.disable_sctp_data_channels = true; - InitWithDtls(GetParam()); - - SetLocalDescriptionWithDataChannel(); - EXPECT_EQ(nullptr, data_engine_->GetChannel(0)); - EXPECT_EQ(nullptr, fake_sctp_transport_factory_->last_fake_sctp_transport()); -} - -TEST_P(WebRtcSessionTest, TestSctpDataChannelSendPortParsing) { - const int new_send_port = 9998; - const int new_recv_port = 7775; - - InitWithDtls(GetParam()); - SetFactoryDtlsSrtp(); - - // By default, don't actually add the codecs to desc_factory_; they don't - // actually get serialized for SCTP in BuildMediaDescription(). Instead, - // let the session description get parsed. That'll get the proper codecs - // into the stream. - cricket::MediaSessionOptions options; - SessionDescriptionInterface* offer = - CreateRemoteOfferWithSctpPort("stream1", new_send_port, options); - - // SetRemoteDescription will take the ownership of the offer. - SetRemoteDescriptionWithoutError(offer); - - SessionDescriptionInterface* answer = - ChangeSDPSctpPort(new_recv_port, CreateAnswer()); - ASSERT_TRUE(answer != NULL); - - // Now set the local description, which'll take ownership of the answer. - SetLocalDescriptionWithoutError(answer); - - // TEST PLAN: Set the port number to something new, set it in the SDP, - // and pass it all the way down. - EXPECT_EQ(nullptr, data_engine_->GetChannel(0)); - CreateDataChannel(); - ASSERT_NE(nullptr, fake_sctp_transport_factory_->last_fake_sctp_transport()); - EXPECT_EQ( - new_recv_port, - fake_sctp_transport_factory_->last_fake_sctp_transport()->local_port()); - EXPECT_EQ( - new_send_port, - fake_sctp_transport_factory_->last_fake_sctp_transport()->remote_port()); -} - -// Verifies that when a session's SctpTransport receives an OPEN message, -// WebRtcSession signals the SctpTransport creation request with the expected -// config. -TEST_P(WebRtcSessionTest, TestSctpDataChannelOpenMessage) { - InitWithDtls(GetParam()); - - SetLocalDescriptionWithDataChannel(); - EXPECT_EQ(nullptr, data_engine_->GetChannel(0)); - ASSERT_NE(nullptr, fake_sctp_transport_factory_->last_fake_sctp_transport()); - - // Make the fake SCTP transport pretend it received an OPEN message. - webrtc::DataChannelInit config; - config.id = 1; - rtc::CopyOnWriteBuffer payload; - webrtc::WriteDataChannelOpenMessage("a", config, &payload); - cricket::ReceiveDataParams params; - params.ssrc = config.id; - params.type = cricket::DMT_CONTROL; - fake_sctp_transport_factory_->last_fake_sctp_transport()->SignalDataReceived( - params, payload); - - EXPECT_EQ_WAIT("a", last_data_channel_label_, kDefaultTimeout); - EXPECT_EQ(config.id, last_data_channel_config_.id); - EXPECT_FALSE(last_data_channel_config_.negotiated); - EXPECT_EQ(webrtc::InternalDataChannelInit::kAcker, - last_data_channel_config_.open_handshake_role); -} - #ifdef HAVE_QUIC TEST_P(WebRtcSessionTest, TestNegotiateQuic) { configuration_.enable_quic = true;