/* * 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 #include #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::_; using ::testing::ByMove; using ::testing::DoAll; using ::testing::ElementsAre; using ::testing::InSequence; using ::testing::Invoke; using ::testing::NiceMock; using ::testing::Return; using ::testing::ReturnPointee; namespace webrtc { namespace { class MockDataChannelObserver : public DataChannelSink { public: MOCK_METHOD(void, OnConnected, ()); // DataChannelSink MOCK_METHOD(void, OnDataReceived, (int, DataMessageType, const rtc::CopyOnWriteBuffer&)); MOCK_METHOD(void, OnChannelClosing, (int)); MOCK_METHOD(void, OnChannelClosed, (int)); MOCK_METHOD(void, OnReadyToSend, ()); MOCK_METHOD(void, OnTransportClosed, (RTCError)); }; class Peer { public: Peer() : fake_packet_transport_("transport"), simulated_clock_(1000) { auto socket_ptr = std::make_unique(); socket_ = socket_ptr.get(); auto mock_dcsctp_socket_factory = std::make_unique(); EXPECT_CALL(*mock_dcsctp_socket_factory, Create) .Times(1) .WillOnce(Return(ByMove(std::move(socket_ptr)))); sctp_transport_ = std::make_unique( rtc::Thread::Current(), &fake_packet_transport_, &simulated_clock_, std::move(mock_dcsctp_socket_factory)); sctp_transport_->SetDataChannelSink(&observer_); sctp_transport_->SetOnConnectedCallback( [this]() { observer_.OnConnected(); }); } rtc::FakePacketTransport fake_packet_transport_; webrtc::SimulatedClock simulated_clock_; dcsctp::MockDcSctpSocket* socket_; std::unique_ptr sctp_transport_; NiceMock 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_, OnReadyToSend); EXPECT_CALL(peer_a.observer_, OnConnected); 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_, OnChannelClosing(1)).Times(0); EXPECT_CALL(peer_b.observer_, OnChannelClosing(1)); EXPECT_CALL(peer_a.observer_, OnChannelClosed(1)); EXPECT_CALL(peer_b.observer_, OnChannelClosed(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_b.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(peer_a.sctp_transport_.get()) ->OnStreamsResetPerformed(streams); static_cast(peer_b.sctp_transport_.get()) ->OnIncomingStreamsReset(streams); static_cast(peer_a.sctp_transport_.get()) ->OnIncomingStreamsReset(streams); static_cast(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 // OnClosingProcedureStartedRemotely 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_, OnChannelClosing(1)).Times(0); EXPECT_CALL(peer_b.observer_, OnChannelClosing(1)).Times(0); EXPECT_CALL(peer_a.observer_, OnChannelClosed(1)); EXPECT_CALL(peer_b.observer_, OnChannelClosed(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_b.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(peer_a.sctp_transport_.get()) ->OnStreamsResetPerformed(streams); static_cast(peer_b.sctp_transport_.get()) ->OnStreamsResetPerformed(streams); static_cast(peer_a.sctp_transport_.get()) ->OnIncomingStreamsReset(streams); static_cast(peer_b.sctp_transport_.get()) ->OnIncomingStreamsReset(streams); } TEST(DcSctpTransportTest, DiscardMessageClosedChannel) { rtc::AutoThread main_thread; Peer peer_a; EXPECT_CALL(*peer_a.socket_, Send(_, _)).Times(0); peer_a.sctp_transport_->Start(5000, 5000, 256 * 1024); cricket::SendDataResult result; SendDataParams params; rtc::CopyOnWriteBuffer payload; bool send_data_return = peer_a.sctp_transport_->SendData(1, params, payload, &result); EXPECT_FALSE(send_data_return); EXPECT_EQ(cricket::SDR_ERROR, result); } TEST(DcSctpTransportTest, DiscardMessageClosingChannel) { rtc::AutoThread main_thread; Peer peer_a; EXPECT_CALL(*peer_a.socket_, Send(_, _)).Times(0); peer_a.sctp_transport_->OpenStream(1); peer_a.sctp_transport_->Start(5000, 5000, 256 * 1024); peer_a.sctp_transport_->ResetStream(1); cricket::SendDataResult result; SendDataParams params; rtc::CopyOnWriteBuffer payload; bool send_data_return = peer_a.sctp_transport_->SendData(1, params, payload, &result); EXPECT_FALSE(send_data_return); EXPECT_EQ(cricket::SDR_ERROR, result); } TEST(DcSctpTransportTest, SendDataOpenChannel) { rtc::AutoThread main_thread; Peer peer_a; dcsctp::DcSctpOptions options; EXPECT_CALL(*peer_a.socket_, Send(_, _)).Times(1); EXPECT_CALL(*peer_a.socket_, options()).WillOnce(ReturnPointee(&options)); peer_a.sctp_transport_->OpenStream(1); peer_a.sctp_transport_->Start(5000, 5000, 256 * 1024); cricket::SendDataResult result; SendDataParams params; rtc::CopyOnWriteBuffer payload; bool send_data_return = peer_a.sctp_transport_->SendData(1, params, payload, &result); EXPECT_TRUE(send_data_return); EXPECT_EQ(cricket::SDR_SUCCESS, result); } } // namespace webrtc