blob: 9d78d1d85f60d5ea59286c3b05997e4b9459eae4 [file] [log] [blame]
/*
* Copyright 2022 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 "media/sctp/dcsctp_transport.h"
#include <memory>
#include <utility>
#include "net/dcsctp/public/mock_dcsctp_socket.h"
#include "net/dcsctp/public/mock_dcsctp_socket_factory.h"
#include "p2p/base/fake_packet_transport.h"
#include "test/gtest.h"
using ::testing::ByMove;
using ::testing::DoAll;
using ::testing::ElementsAre;
using ::testing::InSequence;
using ::testing::Invoke;
using ::testing::NiceMock;
using ::testing::Return;
namespace webrtc {
namespace {
class SctpInternalTransportObserver : public sigslot::has_slots<> {
public:
MOCK_METHOD(void, OnSignalReadyToSendData, ());
MOCK_METHOD(void, OnSignalAssociationChangeCommunicationUp, ());
MOCK_METHOD(void, OnSignalClosingProcedureStartedRemotely, (int));
MOCK_METHOD(void, OnSignalClosingProcedureComplete, (int));
};
class Peer {
public:
Peer() : fake_packet_transport_("transport"), simulated_clock_(1000) {
auto socket_ptr = std::make_unique<dcsctp::MockDcSctpSocket>();
socket_ = socket_ptr.get();
auto mock_dcsctp_socket_factory =
std::make_unique<dcsctp::MockDcSctpSocketFactory>();
EXPECT_CALL(*mock_dcsctp_socket_factory, Create)
.Times(1)
.WillOnce(Return(ByMove(std::move(socket_ptr))));
sctp_transport_ = std::make_unique<webrtc::DcSctpTransport>(
rtc::Thread::Current(), &fake_packet_transport_, &simulated_clock_,
std::move(mock_dcsctp_socket_factory));
sctp_transport_->SignalAssociationChangeCommunicationUp.connect(
static_cast<SctpInternalTransportObserver*>(&observer_),
&SctpInternalTransportObserver::OnSignalReadyToSendData);
sctp_transport_->SignalAssociationChangeCommunicationUp.connect(
static_cast<SctpInternalTransportObserver*>(&observer_),
&SctpInternalTransportObserver::
OnSignalAssociationChangeCommunicationUp);
sctp_transport_->SignalClosingProcedureStartedRemotely.connect(
static_cast<SctpInternalTransportObserver*>(&observer_),
&SctpInternalTransportObserver::
OnSignalClosingProcedureStartedRemotely);
sctp_transport_->SignalClosingProcedureComplete.connect(
static_cast<SctpInternalTransportObserver*>(&observer_),
&SctpInternalTransportObserver::OnSignalClosingProcedureComplete);
}
rtc::FakePacketTransport fake_packet_transport_;
webrtc::SimulatedClock simulated_clock_;
dcsctp::MockDcSctpSocket* socket_;
std::unique_ptr<webrtc::DcSctpTransport> sctp_transport_;
NiceMock<SctpInternalTransportObserver> observer_;
};
} // namespace
TEST(DcSctpTransportTest, OpenSequence) {
rtc::AutoThread main_thread;
Peer peer_a;
peer_a.fake_packet_transport_.SetWritable(true);
EXPECT_CALL(*peer_a.socket_, Connect)
.Times(1)
.WillOnce(Invoke(peer_a.sctp_transport_.get(),
&dcsctp::DcSctpSocketCallbacks::OnConnected));
EXPECT_CALL(peer_a.observer_, OnSignalReadyToSendData);
EXPECT_CALL(peer_a.observer_, OnSignalAssociationChangeCommunicationUp);
peer_a.sctp_transport_->Start(5000, 5000, 256 * 1024);
}
// Tests that the close sequence invoked from one end results in the stream to
// be reset from both ends and all the proper signals are sent.
TEST(DcSctpTransportTest, CloseSequence) {
rtc::AutoThread main_thread;
Peer peer_a;
Peer peer_b;
peer_a.fake_packet_transport_.SetDestination(&peer_b.fake_packet_transport_,
false);
{
InSequence sequence;
EXPECT_CALL(*peer_a.socket_, ResetStreams(ElementsAre(dcsctp::StreamID(1))))
.WillOnce(Return(dcsctp::ResetStreamsStatus::kPerformed));
EXPECT_CALL(*peer_b.socket_, ResetStreams(ElementsAre(dcsctp::StreamID(1))))
.WillOnce(Return(dcsctp::ResetStreamsStatus::kPerformed));
EXPECT_CALL(peer_a.observer_, OnSignalClosingProcedureStartedRemotely(1))
.Times(0);
EXPECT_CALL(peer_b.observer_, OnSignalClosingProcedureStartedRemotely(1));
EXPECT_CALL(peer_a.observer_, OnSignalClosingProcedureComplete(1));
EXPECT_CALL(peer_b.observer_, OnSignalClosingProcedureComplete(1));
}
peer_a.sctp_transport_->Start(5000, 5000, 256 * 1024);
peer_b.sctp_transport_->Start(5000, 5000, 256 * 1024);
peer_a.sctp_transport_->OpenStream(1);
peer_a.sctp_transport_->ResetStream(1);
// Simulate the callbacks from the stream resets
dcsctp::StreamID streams[1] = {dcsctp::StreamID(1)};
static_cast<dcsctp::DcSctpSocketCallbacks*>(peer_a.sctp_transport_.get())
->OnStreamsResetPerformed(streams);
static_cast<dcsctp::DcSctpSocketCallbacks*>(peer_b.sctp_transport_.get())
->OnIncomingStreamsReset(streams);
static_cast<dcsctp::DcSctpSocketCallbacks*>(peer_a.sctp_transport_.get())
->OnIncomingStreamsReset(streams);
static_cast<dcsctp::DcSctpSocketCallbacks*>(peer_b.sctp_transport_.get())
->OnStreamsResetPerformed(streams);
}
// Tests that the close sequence initiated from both peers at the same time
// terminates properly. Both peers will think they initiated it, so no
// OnSignalClosingProcedureStartedRemotely should be called.
TEST(DcSctpTransportTest, CloseSequenceSimultaneous) {
rtc::AutoThread main_thread;
Peer peer_a;
Peer peer_b;
peer_a.fake_packet_transport_.SetDestination(&peer_b.fake_packet_transport_,
false);
{
InSequence sequence;
EXPECT_CALL(*peer_a.socket_, ResetStreams(ElementsAre(dcsctp::StreamID(1))))
.WillOnce(Return(dcsctp::ResetStreamsStatus::kPerformed));
EXPECT_CALL(*peer_b.socket_, ResetStreams(ElementsAre(dcsctp::StreamID(1))))
.WillOnce(Return(dcsctp::ResetStreamsStatus::kPerformed));
EXPECT_CALL(peer_a.observer_, OnSignalClosingProcedureStartedRemotely(1))
.Times(0);
EXPECT_CALL(peer_b.observer_, OnSignalClosingProcedureStartedRemotely(1))
.Times(0);
EXPECT_CALL(peer_a.observer_, OnSignalClosingProcedureComplete(1));
EXPECT_CALL(peer_b.observer_, OnSignalClosingProcedureComplete(1));
}
peer_a.sctp_transport_->Start(5000, 5000, 256 * 1024);
peer_b.sctp_transport_->Start(5000, 5000, 256 * 1024);
peer_a.sctp_transport_->OpenStream(1);
peer_a.sctp_transport_->ResetStream(1);
peer_b.sctp_transport_->ResetStream(1);
// Simulate the callbacks from the stream resets
dcsctp::StreamID streams[1] = {dcsctp::StreamID(1)};
static_cast<dcsctp::DcSctpSocketCallbacks*>(peer_a.sctp_transport_.get())
->OnStreamsResetPerformed(streams);
static_cast<dcsctp::DcSctpSocketCallbacks*>(peer_b.sctp_transport_.get())
->OnStreamsResetPerformed(streams);
static_cast<dcsctp::DcSctpSocketCallbacks*>(peer_a.sctp_transport_.get())
->OnIncomingStreamsReset(streams);
static_cast<dcsctp::DcSctpSocketCallbacks*>(peer_b.sctp_transport_.get())
->OnIncomingStreamsReset(streams);
}
} // namespace webrtc