|  | /* | 
|  | *  Copyright 2019 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 "pc/sctp_transport.h" | 
|  |  | 
|  | #include <utility> | 
|  | #include <vector> | 
|  |  | 
|  | #include "absl/memory/memory.h" | 
|  | #include "p2p/base/fake_dtls_transport.h" | 
|  | #include "pc/dtls_transport.h" | 
|  | #include "rtc_base/gunit.h" | 
|  | #include "test/gmock.h" | 
|  | #include "test/gtest.h" | 
|  |  | 
|  | constexpr int kDefaultTimeout = 1000;  // milliseconds | 
|  | constexpr int kTestMaxSctpStreams = 1234; | 
|  |  | 
|  | using cricket::FakeDtlsTransport; | 
|  | using ::testing::ElementsAre; | 
|  |  | 
|  | namespace webrtc { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | class FakeCricketSctpTransport : public cricket::SctpTransportInternal { | 
|  | public: | 
|  | void SetDtlsTransport(rtc::PacketTransportInternal* transport) override {} | 
|  | bool Start(int local_port, int remote_port, int max_message_size) override { | 
|  | 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 max_message_size() const override { return 0; } | 
|  | absl::optional<int> max_outbound_streams() const override { | 
|  | return max_outbound_streams_; | 
|  | } | 
|  | absl::optional<int> max_inbound_streams() const override { | 
|  | return max_inbound_streams_; | 
|  | } | 
|  | // Methods exposed for testing | 
|  | void SendSignalReadyToSendData() { SignalReadyToSendData(); } | 
|  |  | 
|  | void SendSignalAssociationChangeCommunicationUp() { | 
|  | SignalAssociationChangeCommunicationUp(); | 
|  | } | 
|  |  | 
|  | void SendSignalClosingProcedureStartedRemotely() { | 
|  | SignalClosingProcedureStartedRemotely(1); | 
|  | } | 
|  |  | 
|  | void SendSignalClosingProcedureComplete() { | 
|  | SignalClosingProcedureComplete(1); | 
|  | } | 
|  | void set_max_outbound_streams(int streams) { | 
|  | max_outbound_streams_ = streams; | 
|  | } | 
|  | void set_max_inbound_streams(int streams) { max_inbound_streams_ = streams; } | 
|  |  | 
|  | private: | 
|  | absl::optional<int> max_outbound_streams_; | 
|  | absl::optional<int> max_inbound_streams_; | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | class TestSctpTransportObserver : public SctpTransportObserverInterface { | 
|  | public: | 
|  | TestSctpTransportObserver() : info_(SctpTransportState::kNew) {} | 
|  |  | 
|  | void OnStateChange(SctpTransportInformation info) override { | 
|  | info_ = info; | 
|  | states_.push_back(info.state()); | 
|  | } | 
|  |  | 
|  | SctpTransportState State() { | 
|  | if (states_.size() > 0) { | 
|  | return states_[states_.size() - 1]; | 
|  | } else { | 
|  | return SctpTransportState::kNew; | 
|  | } | 
|  | } | 
|  |  | 
|  | const std::vector<SctpTransportState>& States() { return states_; } | 
|  |  | 
|  | const SctpTransportInformation LastReceivedInformation() { return info_; } | 
|  |  | 
|  | private: | 
|  | std::vector<SctpTransportState> states_; | 
|  | SctpTransportInformation info_; | 
|  | }; | 
|  |  | 
|  | class SctpTransportTest : public ::testing::Test { | 
|  | public: | 
|  | SctpTransport* transport() { return transport_.get(); } | 
|  | SctpTransportObserverInterface* observer() { return &observer_; } | 
|  |  | 
|  | void CreateTransport() { | 
|  | auto cricket_sctp_transport = | 
|  | absl::WrapUnique(new FakeCricketSctpTransport()); | 
|  | transport_ = new rtc::RefCountedObject<SctpTransport>( | 
|  | std::move(cricket_sctp_transport)); | 
|  | } | 
|  |  | 
|  | void AddDtlsTransport() { | 
|  | std::unique_ptr<cricket::DtlsTransportInternal> cricket_transport = | 
|  | absl::make_unique<FakeDtlsTransport>( | 
|  | "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP); | 
|  | dtls_transport_ = | 
|  | new rtc::RefCountedObject<DtlsTransport>(std::move(cricket_transport)); | 
|  | transport_->SetDtlsTransport(dtls_transport_); | 
|  | } | 
|  |  | 
|  | void CompleteSctpHandshake() { | 
|  | CricketSctpTransport()->SendSignalReadyToSendData(); | 
|  | // The computed MaxChannels shall be the minimum of the outgoing | 
|  | // and incoming # of streams. | 
|  | CricketSctpTransport()->set_max_outbound_streams(kTestMaxSctpStreams); | 
|  | CricketSctpTransport()->set_max_inbound_streams(kTestMaxSctpStreams + 1); | 
|  | CricketSctpTransport()->SendSignalAssociationChangeCommunicationUp(); | 
|  | } | 
|  |  | 
|  | FakeCricketSctpTransport* CricketSctpTransport() { | 
|  | return static_cast<FakeCricketSctpTransport*>(transport_->internal()); | 
|  | } | 
|  |  | 
|  | rtc::scoped_refptr<SctpTransport> transport_; | 
|  | rtc::scoped_refptr<DtlsTransport> dtls_transport_; | 
|  | TestSctpTransportObserver observer_; | 
|  | }; | 
|  |  | 
|  | TEST(SctpTransportSimpleTest, CreateClearDelete) { | 
|  | std::unique_ptr<cricket::SctpTransportInternal> fake_cricket_sctp_transport = | 
|  | absl::WrapUnique(new FakeCricketSctpTransport()); | 
|  | rtc::scoped_refptr<SctpTransport> sctp_transport = | 
|  | new rtc::RefCountedObject<SctpTransport>( | 
|  | std::move(fake_cricket_sctp_transport)); | 
|  | ASSERT_TRUE(sctp_transport->internal()); | 
|  | ASSERT_EQ(SctpTransportState::kNew, sctp_transport->Information().state()); | 
|  | sctp_transport->Clear(); | 
|  | ASSERT_FALSE(sctp_transport->internal()); | 
|  | ASSERT_EQ(SctpTransportState::kClosed, sctp_transport->Information().state()); | 
|  | } | 
|  |  | 
|  | TEST_F(SctpTransportTest, EventsObservedWhenConnecting) { | 
|  | CreateTransport(); | 
|  | transport()->RegisterObserver(observer()); | 
|  | AddDtlsTransport(); | 
|  | CompleteSctpHandshake(); | 
|  | ASSERT_EQ_WAIT(SctpTransportState::kConnected, observer_.State(), | 
|  | kDefaultTimeout); | 
|  | EXPECT_THAT(observer_.States(), ElementsAre(SctpTransportState::kConnecting, | 
|  | SctpTransportState::kConnected)); | 
|  | } | 
|  |  | 
|  | TEST_F(SctpTransportTest, CloseWhenClearing) { | 
|  | CreateTransport(); | 
|  | transport()->RegisterObserver(observer()); | 
|  | AddDtlsTransport(); | 
|  | CompleteSctpHandshake(); | 
|  | ASSERT_EQ_WAIT(SctpTransportState::kConnected, observer_.State(), | 
|  | kDefaultTimeout); | 
|  | transport()->Clear(); | 
|  | ASSERT_EQ_WAIT(SctpTransportState::kClosed, observer_.State(), | 
|  | kDefaultTimeout); | 
|  | } | 
|  |  | 
|  | TEST_F(SctpTransportTest, MaxChannelsSignalled) { | 
|  | CreateTransport(); | 
|  | transport()->RegisterObserver(observer()); | 
|  | AddDtlsTransport(); | 
|  | EXPECT_FALSE(transport()->Information().MaxChannels()); | 
|  | EXPECT_FALSE(observer_.LastReceivedInformation().MaxChannels()); | 
|  | CompleteSctpHandshake(); | 
|  | ASSERT_EQ_WAIT(SctpTransportState::kConnected, observer_.State(), | 
|  | kDefaultTimeout); | 
|  | EXPECT_TRUE(transport()->Information().MaxChannels()); | 
|  | EXPECT_EQ(kTestMaxSctpStreams, *(transport()->Information().MaxChannels())); | 
|  | EXPECT_TRUE(observer_.LastReceivedInformation().MaxChannels()); | 
|  | EXPECT_EQ(kTestMaxSctpStreams, | 
|  | *(observer_.LastReceivedInformation().MaxChannels())); | 
|  | } | 
|  |  | 
|  | }  // namespace webrtc |