diff --git a/pc/BUILD.gn b/pc/BUILD.gn index 6339ffbfbb..fa182fa811 100644 --- a/pc/BUILD.gn +++ b/pc/BUILD.gn @@ -387,6 +387,8 @@ if (rtc_include_tests) { "peerconnectionendtoend_unittest.cc", "peerconnectionfactory_unittest.cc", "peerconnectioninterface_unittest.cc", + "peerconnectionwrapper.cc", + "peerconnectionwrapper.h", "proxy_unittest.cc", "rtcstats_integrationtest.cc", "rtcstatscollector_unittest.cc", diff --git a/pc/peerconnection_integrationtest.cc b/pc/peerconnection_integrationtest.cc index 641b2fa7a5..5ebf0315fa 100644 --- a/pc/peerconnection_integrationtest.cc +++ b/pc/peerconnection_integrationtest.cc @@ -158,6 +158,8 @@ class MockRtpReceiverObserver : public webrtc::RtpReceiverObserverInterface { // Uses a fake network, fake A/V capture, and optionally fake // encoders/decoders, though they aren't used by default since they don't // advertise support of any codecs. +// TODO(steveanton): See how this could become a subclass of +// PeerConnectionWrapper defined in peerconnectionwrapper.h . class PeerConnectionWrapper : public webrtc::PeerConnectionObserver, public SignalingMessageReceiver, public ObserverInterface { diff --git a/pc/peerconnectioninterface_unittest.cc b/pc/peerconnectioninterface_unittest.cc index b8bf8b8435..cc063b4f74 100644 --- a/pc/peerconnectioninterface_unittest.cc +++ b/pc/peerconnectioninterface_unittest.cc @@ -336,6 +336,7 @@ using webrtc::MediaStreamInterface; using webrtc::MediaStreamTrackInterface; using webrtc::MockCreateSessionDescriptionObserver; using webrtc::MockDataChannelObserver; +using webrtc::MockPeerConnectionObserver; using webrtc::MockSetSessionDescriptionObserver; using webrtc::MockStatsObserver; using webrtc::NotifierInterface; @@ -527,113 +528,6 @@ class MockTrackObserver : public ObserverInterface { NotifierInterface* notifier_; }; -class MockPeerConnectionObserver : public PeerConnectionObserver { - public: - MockPeerConnectionObserver() : remote_streams_(StreamCollection::Create()) {} - virtual ~MockPeerConnectionObserver() { - } - void SetPeerConnectionInterface(PeerConnectionInterface* pc) { - pc_ = pc; - if (pc) { - state_ = pc_->signaling_state(); - } - } - void OnSignalingChange( - PeerConnectionInterface::SignalingState new_state) override { - EXPECT_EQ(pc_->signaling_state(), new_state); - state_ = new_state; - } - - MediaStreamInterface* RemoteStream(const std::string& label) { - return remote_streams_->find(label); - } - StreamCollectionInterface* remote_streams() const { return remote_streams_; } - void OnAddStream(rtc::scoped_refptr stream) override { - last_added_stream_ = stream; - remote_streams_->AddStream(stream); - } - void OnRemoveStream( - rtc::scoped_refptr stream) override { - last_removed_stream_ = stream; - remote_streams_->RemoveStream(stream); - } - void OnRenegotiationNeeded() override { renegotiation_needed_ = true; } - void OnDataChannel( - rtc::scoped_refptr data_channel) override { - last_datachannel_ = data_channel; - } - - void OnIceConnectionChange( - PeerConnectionInterface::IceConnectionState new_state) override { - EXPECT_EQ(pc_->ice_connection_state(), new_state); - callback_triggered_ = true; - } - void OnIceGatheringChange( - PeerConnectionInterface::IceGatheringState new_state) override { - EXPECT_EQ(pc_->ice_gathering_state(), new_state); - ice_complete_ = new_state == PeerConnectionInterface::kIceGatheringComplete; - callback_triggered_ = true; - } - void OnIceCandidate(const webrtc::IceCandidateInterface* candidate) override { - EXPECT_NE(PeerConnectionInterface::kIceGatheringNew, - pc_->ice_gathering_state()); - - std::string sdp; - EXPECT_TRUE(candidate->ToString(&sdp)); - EXPECT_LT(0u, sdp.size()); - last_candidate_.reset(webrtc::CreateIceCandidate(candidate->sdp_mid(), - candidate->sdp_mline_index(), sdp, NULL)); - EXPECT_TRUE(last_candidate_.get() != NULL); - callback_triggered_ = true; - } - - void OnIceCandidatesRemoved( - const std::vector& candidates) override { - callback_triggered_ = true; - } - - void OnIceConnectionReceivingChange(bool receiving) override { - callback_triggered_ = true; - } - - void OnAddTrack( - rtc::scoped_refptr receiver, - const std::vector>& - streams) override { - EXPECT_TRUE(receiver != nullptr); - num_added_tracks_++; - last_added_track_label_ = receiver->id(); - } - - // Returns the label of the last added stream. - // Empty string if no stream have been added. - std::string GetLastAddedStreamLabel() { - if (last_added_stream_.get()) - return last_added_stream_->label(); - return ""; - } - std::string GetLastRemovedStreamLabel() { - if (last_removed_stream_.get()) - return last_removed_stream_->label(); - return ""; - } - - rtc::scoped_refptr pc_; - PeerConnectionInterface::SignalingState state_; - std::unique_ptr last_candidate_; - rtc::scoped_refptr last_datachannel_; - rtc::scoped_refptr remote_streams_; - bool renegotiation_needed_ = false; - bool ice_complete_ = false; - bool callback_triggered_ = false; - int num_added_tracks_ = 0; - std::string last_added_track_label_; - - private: - rtc::scoped_refptr last_added_stream_; - rtc::scoped_refptr last_removed_stream_; -}; - } // namespace // The PeerConnectionMediaConfig tests below verify that configuration and @@ -691,6 +585,8 @@ class PeerConnectionFactoryForTest : public webrtc::PeerConnectionFactory { cricket::TransportController* transport_controller; }; +// TODO(steveanton): Convert to use the new PeerConnectionUnitTestFixture and +// PeerConnectionWrapper. class PeerConnectionInterfaceTest : public testing::Test { protected: PeerConnectionInterfaceTest() diff --git a/pc/peerconnectionwrapper.cc b/pc/peerconnectionwrapper.cc new file mode 100644 index 0000000000..01710d9cb2 --- /dev/null +++ b/pc/peerconnectionwrapper.cc @@ -0,0 +1,178 @@ +/* + * 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 "pc/peerconnectionwrapper.h" + +#include +#include +#include + +#include "api/jsepsessiondescription.h" +#include "media/base/fakevideocapturer.h" +#include "rtc_base/gunit.h" +#include "rtc_base/ptr_util.h" + +namespace webrtc { + +namespace { +const uint32_t kWaitTimeout = 10000U; +} + +PeerConnectionWrapper::PeerConnectionWrapper( + rtc::scoped_refptr pc_factory, + rtc::scoped_refptr pc, + std::unique_ptr observer) + : pc_factory_(pc_factory), pc_(pc), observer_(std::move(observer)) { + RTC_DCHECK(pc_factory_); + RTC_DCHECK(pc_); + RTC_DCHECK(observer_); + observer_->SetPeerConnectionInterface(pc_.get()); +} + +PeerConnectionWrapper::~PeerConnectionWrapper() = default; + +PeerConnectionFactoryInterface* PeerConnectionWrapper::pc_factory() { + return pc_factory_.get(); +} + +PeerConnectionInterface* PeerConnectionWrapper::pc() { + return pc_.get(); +} + +MockPeerConnectionObserver* PeerConnectionWrapper::observer() { + return observer_.get(); +} + +std::unique_ptr +PeerConnectionWrapper::CreateOffer() { + return CreateOffer(PeerConnectionInterface::RTCOfferAnswerOptions()); +} + +std::unique_ptr PeerConnectionWrapper::CreateOffer( + const PeerConnectionInterface::RTCOfferAnswerOptions& options) { + return CreateSdp([this, options](CreateSessionDescriptionObserver* observer) { + pc()->CreateOffer(observer, options); + }); +} + +std::unique_ptr +PeerConnectionWrapper::CreateOfferAndSetAsLocal() { + auto offer = CreateOffer(); + if (!offer) { + return nullptr; + } + EXPECT_TRUE(SetLocalDescription(CloneSessionDescription(offer.get()))); + return offer; +} + +std::unique_ptr +PeerConnectionWrapper::CreateAnswer() { + return CreateAnswer(PeerConnectionInterface::RTCOfferAnswerOptions()); +} + +std::unique_ptr +PeerConnectionWrapper::CreateAnswer( + const PeerConnectionInterface::RTCOfferAnswerOptions& options) { + return CreateSdp([this, options](CreateSessionDescriptionObserver* observer) { + pc()->CreateAnswer(observer, options); + }); +} + +std::unique_ptr +PeerConnectionWrapper::CreateAnswerAndSetAsLocal() { + auto answer = CreateAnswer(); + if (!answer) { + return nullptr; + } + EXPECT_TRUE(SetLocalDescription(CloneSessionDescription(answer.get()))); + return answer; +} + +std::unique_ptr PeerConnectionWrapper::CreateSdp( + std::function fn) { + rtc::scoped_refptr observer( + new rtc::RefCountedObject()); + fn(observer); + EXPECT_EQ_WAIT(true, observer->called(), kWaitTimeout); + return observer->MoveDescription(); +} + +bool PeerConnectionWrapper::SetLocalDescription( + std::unique_ptr desc) { + return SetSdp([this, &desc](SetSessionDescriptionObserver* observer) { + pc()->SetLocalDescription(observer, desc.release()); + }); +} + +bool PeerConnectionWrapper::SetRemoteDescription( + std::unique_ptr desc) { + return SetSdp([this, &desc](SetSessionDescriptionObserver* observer) { + pc()->SetRemoteDescription(observer, desc.release()); + }); +} + +bool PeerConnectionWrapper::SetSdp( + std::function fn) { + rtc::scoped_refptr observer( + new rtc::RefCountedObject()); + fn(observer); + if (pc()->signaling_state() != PeerConnectionInterface::kClosed) { + EXPECT_EQ_WAIT(true, observer->called(), kWaitTimeout); + } + return observer->result(); +} + +std::unique_ptr +PeerConnectionWrapper::CloneSessionDescription( + const SessionDescriptionInterface* desc) { + std::unique_ptr clone( + new JsepSessionDescription(desc->type())); + RTC_DCHECK(clone->Initialize(desc->description()->Copy(), desc->session_id(), + desc->session_version())); + return clone; +} + +void PeerConnectionWrapper::AddAudioStream(const std::string& stream_label, + const std::string& track_label) { + auto stream = pc_factory()->CreateLocalMediaStream(stream_label); + auto audio_track = pc_factory()->CreateAudioTrack(track_label, nullptr); + EXPECT_TRUE(pc()->AddTrack(audio_track, {stream})); + EXPECT_TRUE_WAIT(observer()->renegotiation_needed_, kWaitTimeout); + observer()->renegotiation_needed_ = false; +} + +void PeerConnectionWrapper::AddVideoStream(const std::string& stream_label, + const std::string& track_label) { + auto stream = pc_factory()->CreateLocalMediaStream(stream_label); + auto video_source = pc_factory()->CreateVideoSource( + rtc::MakeUnique()); + auto video_track = pc_factory()->CreateVideoTrack(track_label, video_source); + EXPECT_TRUE(pc()->AddTrack(video_track, {stream})); + EXPECT_TRUE_WAIT(observer()->renegotiation_needed_, kWaitTimeout); + observer()->renegotiation_needed_ = false; +} + +void PeerConnectionWrapper::AddAudioVideoStream( + const std::string& stream_label, + const std::string& audio_track_label, + const std::string& video_track_label) { + auto stream = pc_factory()->CreateLocalMediaStream(stream_label); + auto audio_track = pc_factory()->CreateAudioTrack(audio_track_label, nullptr); + EXPECT_TRUE(pc()->AddTrack(audio_track, {stream})); + auto video_source = pc_factory()->CreateVideoSource( + rtc::MakeUnique()); + auto video_track = + pc_factory()->CreateVideoTrack(video_track_label, video_source); + EXPECT_TRUE(pc()->AddTrack(video_track, {stream})); + EXPECT_TRUE_WAIT(observer()->renegotiation_needed_, kWaitTimeout); + observer()->renegotiation_needed_ = false; +} + +} // namespace webrtc diff --git a/pc/peerconnectionwrapper.h b/pc/peerconnectionwrapper.h new file mode 100644 index 0000000000..83b81865ab --- /dev/null +++ b/pc/peerconnectionwrapper.h @@ -0,0 +1,108 @@ +/* + * 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_PEERCONNECTIONWRAPPER_H_ +#define PC_PEERCONNECTIONWRAPPER_H_ + +#include +#include +#include + +#include "api/peerconnectioninterface.h" +#include "pc/test/mockpeerconnectionobservers.h" + +namespace webrtc { + +// Class that wraps a PeerConnection so that it is easier to use in unit tests. +// Namely, gives a synchronous API for the event-callback-based API of +// PeerConnection and provides an observer object that stores information from +// PeerConnectionObserver callbacks. +// +// This is intended to be subclassed if additional information needs to be +// stored with the PeerConnection (e.g., fake PeerConnection parameters so that +// tests can be written against those interactions). The base +// PeerConnectionWrapper should only have helper methods that are broadly +// useful. More specific helper methods should be created in the test-specific +// subclass. +// +// The wrapper is intended to be constructed by specialized factory methods on +// a test fixture class then used as a local variable in each test case. +class PeerConnectionWrapper { + public: + // Constructs a PeerConnectionWrapper from the given PeerConnection. + // The given PeerConnectionFactory should be the factory that created the + // PeerConnection and the MockPeerConnectionObserver should be the observer + // that is watching the PeerConnection. + PeerConnectionWrapper( + rtc::scoped_refptr pc_factory, + rtc::scoped_refptr pc, + std::unique_ptr observer); + virtual ~PeerConnectionWrapper(); + + PeerConnectionFactoryInterface* pc_factory(); + PeerConnectionInterface* pc(); + MockPeerConnectionObserver* observer(); + + // Calls the underlying PeerConnection's CreateOffer method and returns the + // resulting SessionDescription once it is available. If the method call + // failed, null is returned. + std::unique_ptr CreateOffer( + const PeerConnectionInterface::RTCOfferAnswerOptions& options); + // Calls CreateOffer with default options. + std::unique_ptr CreateOffer(); + // Calls CreateOffer and sets a copy of the offer as the local description. + std::unique_ptr CreateOfferAndSetAsLocal(); + + // Calls the underlying PeerConnection's CreateAnswer method and returns the + // resulting SessionDescription once it is available. If the method call + // failed, null is returned. + std::unique_ptr CreateAnswer( + const PeerConnectionInterface::RTCOfferAnswerOptions& options); + // Calls CreateAnswer with the default options. + std::unique_ptr CreateAnswer(); + // Calls CreateAnswer and sets a copy of the offer as the local description. + std::unique_ptr CreateAnswerAndSetAsLocal(); + + // Calls the underlying PeerConnection's SetLocalDescription method with the + // given session description and waits for the success/failure response. + // Returns true if the description was successfully set. + bool SetLocalDescription(std::unique_ptr desc); + // Calls the underlying PeerConnection's SetRemoteDescription method with the + // given session description and waits for the success/failure response. + // Returns true if the description was successfully set. + bool SetRemoteDescription(std::unique_ptr desc); + + // Adds a new stream with one audio track to the underlying PeerConnection. + void AddAudioStream(const std::string& stream_label, + const std::string& track_label); + // Adds a new stream with one video track to the underlying PeerConnection. + void AddVideoStream(const std::string& stream_label, + const std::string& track_label); + // Adds a new stream with one audio and one video track to the underlying + // PeerConnection. + void AddAudioVideoStream(const std::string& stream_label, + const std::string& audio_track_label, + const std::string& video_track_label); + + private: + std::unique_ptr CreateSdp( + std::function fn); + bool SetSdp(std::function fn); + std::unique_ptr CloneSessionDescription( + const SessionDescriptionInterface* desc); + + rtc::scoped_refptr pc_factory_; + rtc::scoped_refptr pc_; + std::unique_ptr observer_; +}; + +} // namespace webrtc + +#endif // PC_PEERCONNECTIONWRAPPER_H_ diff --git a/pc/test/mockpeerconnectionobservers.h b/pc/test/mockpeerconnectionobservers.h index e8e78ad7cb..64f84b6b8e 100644 --- a/pc/test/mockpeerconnectionobservers.h +++ b/pc/test/mockpeerconnectionobservers.h @@ -9,6 +9,7 @@ */ // This file contains mock implementations of observers used in PeerConnection. +// TODO(steveanton): These aren't really mocks and should be renamed. #ifndef PC_TEST_MOCKPEERCONNECTIONOBSERVERS_H_ #define PC_TEST_MOCKPEERCONNECTIONOBSERVERS_H_ @@ -17,10 +18,118 @@ #include #include "api/datachannelinterface.h" +#include "pc/streamcollection.h" #include "rtc_base/checks.h" namespace webrtc { +class MockPeerConnectionObserver : public PeerConnectionObserver { + public: + MockPeerConnectionObserver() : remote_streams_(StreamCollection::Create()) {} + virtual ~MockPeerConnectionObserver() {} + void SetPeerConnectionInterface(PeerConnectionInterface* pc) { + pc_ = pc; + if (pc) { + state_ = pc_->signaling_state(); + } + } + void OnSignalingChange( + PeerConnectionInterface::SignalingState new_state) override { + RTC_DCHECK(pc_->signaling_state() == new_state); + state_ = new_state; + } + + MediaStreamInterface* RemoteStream(const std::string& label) { + return remote_streams_->find(label); + } + StreamCollectionInterface* remote_streams() const { return remote_streams_; } + void OnAddStream(rtc::scoped_refptr stream) override { + last_added_stream_ = stream; + remote_streams_->AddStream(stream); + } + void OnRemoveStream( + rtc::scoped_refptr stream) override { + last_removed_stream_ = stream; + remote_streams_->RemoveStream(stream); + } + void OnRenegotiationNeeded() override { renegotiation_needed_ = true; } + void OnDataChannel( + rtc::scoped_refptr data_channel) override { + last_datachannel_ = data_channel; + } + + void OnIceConnectionChange( + PeerConnectionInterface::IceConnectionState new_state) override { + RTC_DCHECK(pc_->ice_connection_state() == new_state); + callback_triggered_ = true; + } + void OnIceGatheringChange( + PeerConnectionInterface::IceGatheringState new_state) override { + RTC_DCHECK(pc_->ice_gathering_state() == new_state); + ice_complete_ = new_state == PeerConnectionInterface::kIceGatheringComplete; + callback_triggered_ = true; + } + void OnIceCandidate(const webrtc::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_); + callback_triggered_ = true; + } + + void OnIceCandidatesRemoved( + const std::vector& candidates) override { + callback_triggered_ = true; + } + + void OnIceConnectionReceivingChange(bool receiving) override { + callback_triggered_ = true; + } + + void OnAddTrack( + rtc::scoped_refptr receiver, + const std::vector>& + streams) override { + RTC_DCHECK(receiver); + num_added_tracks_++; + last_added_track_label_ = receiver->id(); + } + + // Returns the label of the last added stream. + // Empty string if no stream have been added. + std::string GetLastAddedStreamLabel() { + if (last_added_stream_.get()) + return last_added_stream_->label(); + return ""; + } + std::string GetLastRemovedStreamLabel() { + if (last_removed_stream_.get()) + return last_removed_stream_->label(); + return ""; + } + + rtc::scoped_refptr pc_; + PeerConnectionInterface::SignalingState state_; + std::unique_ptr last_candidate_; + rtc::scoped_refptr last_datachannel_; + rtc::scoped_refptr remote_streams_; + bool renegotiation_needed_ = false; + bool ice_complete_ = false; + bool callback_triggered_ = false; + int num_added_tracks_ = 0; + std::string last_added_track_label_; + + private: + rtc::scoped_refptr last_added_stream_; + rtc::scoped_refptr last_removed_stream_; +}; + class MockCreateSessionDescriptionObserver : public webrtc::CreateSessionDescriptionObserver { public: