diff --git a/pc/BUILD.gn b/pc/BUILD.gn index afa08a3079..a5703b77bb 100644 --- a/pc/BUILD.gn +++ b/pc/BUILD.gn @@ -435,6 +435,7 @@ if (rtc_include_tests) { "peerconnection_bundle_unittest.cc", "peerconnection_crypto_unittest.cc", "peerconnection_datachannel_unittest.cc", + "peerconnection_histogram_unittest.cc", "peerconnection_ice_unittest.cc", "peerconnection_integrationtest.cc", "peerconnection_jsep_unittest.cc", diff --git a/pc/peerconnection.cc b/pc/peerconnection.cc index 5526d7266a..f4dcb51fd4 100644 --- a/pc/peerconnection.cc +++ b/pc/peerconnection.cc @@ -106,8 +106,11 @@ enum { MSG_CREATE_SESSIONDESCRIPTION_FAILED, MSG_GETSTATS, MSG_FREE_DATACHANNELS, + MSG_REPORT_USAGE_PATTERN, }; +static const int REPORT_USAGE_PATTERN_DELAY_MS = 60000; + struct SetSessionDescriptionMsg : public rtc::MessageData { explicit SetSessionDescriptionMsg( webrtc::SetSessionDescriptionObserver* observer) @@ -1037,6 +1040,10 @@ bool PeerConnection::Initialize( RtpTransceiverProxyWithInternal::Create( signaling_thread(), new RtpTransceiver(cricket::MEDIA_TYPE_VIDEO))); } + signaling_thread()->PostDelayed( + RTC_FROM_HERE, + return_histogram_very_quickly_ ? 0 : REPORT_USAGE_PATTERN_DELAY_MS, this, + MSG_REPORT_USAGE_PATTERN, nullptr); return true; } @@ -3300,6 +3307,10 @@ void PeerConnection::OnMessage(rtc::Message* msg) { sctp_data_channels_to_free_.clear(); break; } + case MSG_REPORT_USAGE_PATTERN: { + ReportUsagePattern(); + break; + } default: RTC_NOTREACHED() << "Not implemented"; break; diff --git a/pc/peerconnection.h b/pc/peerconnection.h index 0e8b71d449..8901848d9b 100644 --- a/pc/peerconnection.h +++ b/pc/peerconnection.h @@ -265,6 +265,10 @@ class PeerConnection : public PeerConnectionInternal, bool NeedsIceRestart(const std::string& content_name) const override; bool GetSslRole(const std::string& content_name, rtc::SSLRole* role) override; + void ReturnHistogramVeryQuicklyForTesting() { + return_histogram_very_quickly_ = true; + } + protected: ~PeerConnection() override; @@ -1017,6 +1021,7 @@ class PeerConnection : public PeerConnectionInternal, cricket::VideoOptions video_options_; int usage_event_accumulator_ = 0; + bool return_histogram_very_quickly_ = false; }; } // namespace webrtc diff --git a/pc/peerconnection_histogram_unittest.cc b/pc/peerconnection_histogram_unittest.cc new file mode 100644 index 0000000000..b554e86993 --- /dev/null +++ b/pc/peerconnection_histogram_unittest.cc @@ -0,0 +1,145 @@ +/* + * 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/fakemetricsobserver.h" +#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" +#include "pc/sdputils.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; + +static constexpr int kDefaultTimeout = 10000; + +int MakeUsageFingerprint(std::set events) { + int signature = 0; + for (const auto it : events) { + signature |= static_cast(it); + } + return signature; +} + +class PeerConnectionFactoryForUsageHistogramTest + : public rtc::RefCountedObject { + public: + PeerConnectionFactoryForUsageHistogramTest() + : rtc::RefCountedObject( + rtc::Thread::Current(), + rtc::Thread::Current(), + rtc::Thread::Current(), + rtc::MakeUnique(), + CreateCallFactory(), + nullptr) {} + + void ActionsBeforeInitializeForTesting(PeerConnectionInterface* pc) override { + PeerConnection* internal_pc = static_cast(pc); + if (return_histogram_very_quickly_) { + internal_pc->ReturnHistogramVeryQuicklyForTesting(); + } + } + + void ReturnHistogramVeryQuickly() { return_histogram_very_quickly_ = true; } + + private: + bool return_histogram_very_quickly_; +}; + +class PeerConnectionWrapperForUsageHistogramTest + : public PeerConnectionWrapper { + public: + using PeerConnectionWrapper::PeerConnectionWrapper; + + PeerConnection* GetInternalPeerConnection() { + auto* pci = + static_cast*>( + pc()); + return static_cast(pci->internal()); + } +}; + +class PeerConnectionUsageHistogramTest : public ::testing::Test { + protected: + typedef std::unique_ptr + WrapperPtr; + + PeerConnectionUsageHistogramTest() + : vss_(new rtc::VirtualSocketServer()), main_(vss_.get()) { +#ifdef WEBRTC_ANDROID + InitializeAndroidObjects(); +#endif + } + + WrapperPtr CreatePeerConnection() { + return CreatePeerConnection( + RTCConfiguration(), PeerConnectionFactoryInterface::Options(), false); + } + + WrapperPtr CreatePeerConnectionWithImmediateReport() { + return CreatePeerConnection( + RTCConfiguration(), PeerConnectionFactoryInterface::Options(), true); + } + + WrapperPtr CreatePeerConnection( + const RTCConfiguration& config, + const PeerConnectionFactoryInterface::Options factory_options, + bool immediate_report) { + rtc::scoped_refptr pc_factory( + new PeerConnectionFactoryForUsageHistogramTest()); + pc_factory->SetOptions(factory_options); + RTC_CHECK(pc_factory->Initialize()); + if (immediate_report) { + pc_factory->ReturnHistogramVeryQuickly(); + } + 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)); + return wrapper; + } + + std::unique_ptr vss_; + rtc::AutoSocketServerThread main_; +}; + +TEST_F(PeerConnectionUsageHistogramTest, UsageFingerprintHistogramFromTimeout) { + auto pc = CreatePeerConnectionWithImmediateReport(); + + // Register UMA observer before signaling begins. + rtc::scoped_refptr caller_observer = + new rtc::RefCountedObject(); + pc->GetInternalPeerConnection()->RegisterUMAObserver(caller_observer); + int expected_fingerprint = MakeUsageFingerprint({}); + ASSERT_TRUE_WAIT(caller_observer->ExpectOnlySingleEnumCount( + webrtc::kEnumCounterUsagePattern, expected_fingerprint), + kDefaultTimeout); +} + +} // namespace webrtc diff --git a/pc/peerconnectionfactory.cc b/pc/peerconnectionfactory.cc index 10688a3e31..7e48a2fad0 100644 --- a/pc/peerconnectionfactory.cc +++ b/pc/peerconnectionfactory.cc @@ -338,7 +338,7 @@ PeerConnectionFactory::CreatePeerConnection( rtc::scoped_refptr pc( new rtc::RefCountedObject(this, std::move(event_log), std::move(call))); - + ActionsBeforeInitializeForTesting(pc); if (!pc->Initialize(configuration, std::move(dependencies))) { return nullptr; } diff --git a/pc/peerconnectionfactory.h b/pc/peerconnectionfactory.h index d1893a99b1..af3fa2468b 100644 --- a/pc/peerconnectionfactory.h +++ b/pc/peerconnectionfactory.h @@ -122,6 +122,10 @@ class PeerConnectionFactory : public PeerConnectionFactoryInterface { explicit PeerConnectionFactory( PeerConnectionFactoryDependencies dependencies); + // Hook to let testing framework insert actions between + // "new RTCPeerConnection" and "pc.Initialize" + virtual void ActionsBeforeInitializeForTesting(PeerConnectionInterface*) {} + virtual ~PeerConnectionFactory(); private: