diff options
7 files changed, 417 insertions, 10 deletions
diff --git a/content/renderer/media/mock_media_stream_dependency_factory.cc b/content/renderer/media/mock_media_stream_dependency_factory.cc index 3584c79..cb17df2 100644 --- a/content/renderer/media/mock_media_stream_dependency_factory.cc +++ b/content/renderer/media/mock_media_stream_dependency_factory.cc @@ -29,9 +29,11 @@ class MockMediaStreamTrackList virtual size_t count() const OVERRIDE { return tracks_.size(); } + virtual TrackType* at(size_t index) OVERRIDE { return tracks_[index]; } + void AddTrack(TrackType* track) { tracks_.push_back(track); } diff --git a/content/renderer/media/mock_peer_connection_impl.cc b/content/renderer/media/mock_peer_connection_impl.cc index bb3783c..1c243d7 100644 --- a/content/renderer/media/mock_peer_connection_impl.cc +++ b/content/renderer/media/mock_peer_connection_impl.cc @@ -109,13 +109,6 @@ bool MockPeerConnectionImpl::SendDtmf( return false; } -bool MockPeerConnectionImpl::GetStats( - webrtc::StatsObserver* observer, - webrtc::MediaStreamTrackInterface* track) { - NOTIMPLEMENTED(); - return false; -} - void MockPeerConnectionImpl::RemoveStream( MediaStreamInterface* local_stream) { DCHECK_EQ(stream_label_, local_stream->label()); @@ -123,6 +116,36 @@ void MockPeerConnectionImpl::RemoveStream( local_streams_->RemoveStream(local_stream); } +bool MockPeerConnectionImpl::GetStats( + webrtc::StatsObserver* observer, + webrtc::MediaStreamTrackInterface* track) { + std::vector<webrtc::StatsReport> reports; + webrtc::StatsReport report; + report.id = "1234"; + report.type = "ssrc"; + report.local.timestamp = 42; + webrtc::StatsElement::Value value; + value.name = "trackname"; + value.value = "trackvalue"; + report.local.values.push_back(value); + reports.push_back(report); + // If selector is given, we pass back one report. + // If selector is not given, we pass back two. + if (!track) { + report.id = "nontrack"; + report.type = "generic"; + report.local.timestamp = 44; + value.name = "somename"; + value.value = "somevalue"; + report.local.values.push_back(value); + reports.push_back(report); + } + // Note that the callback is synchronous, not asynchronous; it will + // happen before the request call completes. + observer->OnComplete(reports); + return true; +} + MockPeerConnectionImpl::ReadyState MockPeerConnectionImpl::ready_state() { return ready_state_; } diff --git a/content/renderer/media/peer_connection_handler_base.cc b/content/renderer/media/peer_connection_handler_base.cc index fb38a1b..c72a824 100644 --- a/content/renderer/media/peer_connection_handler_base.cc +++ b/content/renderer/media/peer_connection_handler_base.cc @@ -8,12 +8,14 @@ #include "base/utf_string_conversions.h" #include "content/renderer/media/media_stream_dependency_factory.h" #include "content/renderer/media/media_stream_extra_data.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebMediaStreamComponent.h" #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebMediaStreamDescriptor.h" #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebMediaStreamSource.h" #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h" namespace content { +// TODO(hta): Unify implementations of these functions from MediaStreamCenter static webrtc::LocalMediaStreamInterface* GetLocalNativeMediaStream( const WebKit::WebMediaStreamDescriptor& stream) { MediaStreamExtraData* extra_data = @@ -23,6 +25,18 @@ static webrtc::LocalMediaStreamInterface* GetLocalNativeMediaStream( return NULL; } +template <class TrackList> +static webrtc::MediaStreamTrackInterface* GetLocalTrack( + const std::string& source_id, + TrackList* tracks) { + // TODO(hta): Look at MediaStreamTrackInterface - lookup by ID belongs below. + for (size_t i = 0; i < tracks->count(); ++i) { + if (tracks->at(i)->label() == source_id) + return tracks->at(i); + } + return NULL; +} + PeerConnectionHandlerBase::PeerConnectionHandlerBase( MediaStreamDependencyFactory* dependency_factory) : dependency_factory_(dependency_factory), @@ -88,4 +102,26 @@ PeerConnectionHandlerBase::CreateWebKitStreamDescriptor( return descriptor; } +webrtc::MediaStreamTrackInterface* +PeerConnectionHandlerBase::GetLocalNativeMediaStreamTrack( + const WebKit::WebMediaStreamDescriptor& stream, + const WebKit::WebMediaStreamComponent& component) { + std::string source_id = UTF16ToUTF8(component.source().id()); + webrtc::MediaStreamInterface* native_stream + = GetLocalNativeMediaStream(stream); + if (!native_stream) { + return NULL; + } + if (component.source().type() == WebKit::WebMediaStreamSource::TypeAudio) { + return GetLocalTrack<webrtc::AudioTracks>( + source_id, native_stream->audio_tracks()); + } + if (component.source().type() == WebKit::WebMediaStreamSource::TypeVideo) { + return GetLocalTrack<webrtc::VideoTracks>( + source_id, native_stream->video_tracks()); + } + NOTIMPLEMENTED(); // We have an unknown type of media stream track. + return NULL; +} + } // namespace content diff --git a/content/renderer/media/peer_connection_handler_base.h b/content/renderer/media/peer_connection_handler_base.h index 7ccf42a..5399893 100644 --- a/content/renderer/media/peer_connection_handler_base.h +++ b/content/renderer/media/peer_connection_handler_base.h @@ -36,6 +36,9 @@ class CONTENT_EXPORT PeerConnectionHandlerBase void RemoveStream(const WebKit::WebMediaStreamDescriptor& stream); WebKit::WebMediaStreamDescriptor CreateWebKitStreamDescriptor( webrtc::MediaStreamInterface* stream); + webrtc::MediaStreamTrackInterface* GetLocalNativeMediaStreamTrack( + const WebKit::WebMediaStreamDescriptor& stream, + const WebKit::WebMediaStreamComponent& component); // dependency_factory_ is a raw pointer, and is valid for the lifetime of // MediaStreamImpl. diff --git a/content/renderer/media/rtc_peer_connection_handler.cc b/content/renderer/media/rtc_peer_connection_handler.cc index 33bb7c4a..cbcc7e8 100644 --- a/content/renderer/media/rtc_peer_connection_handler.cc +++ b/content/renderer/media/rtc_peer_connection_handler.cc @@ -13,11 +13,14 @@ #include "content/renderer/media/media_stream_dependency_factory.h" #include "content/renderer/media/rtc_media_constraints.h" #include "third_party/WebKit/Source/Platform/chromium/public/WebMediaConstraints.h" +// TODO(hta): Move the following include to WebRTCStatsRequest.h file. +#include "third_party/WebKit/Source/Platform/chromium/public/WebMediaStreamComponent.h" #include "third_party/WebKit/Source/Platform/chromium/public/WebRTCConfiguration.h" #include "third_party/WebKit/Source/Platform/chromium/public/WebRTCICECandidate.h" #include "third_party/WebKit/Source/Platform/chromium/public/WebRTCPeerConnectionHandlerClient.h" #include "third_party/WebKit/Source/Platform/chromium/public/WebRTCSessionDescription.h" #include "third_party/WebKit/Source/Platform/chromium/public/WebRTCSessionDescriptionRequest.h" +#include "third_party/WebKit/Source/Platform/chromium/public/WebRTCStatsRequest.h" #include "third_party/WebKit/Source/Platform/chromium/public/WebRTCVoidRequest.h" #include "third_party/WebKit/Source/Platform/chromium/public/WebURL.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" @@ -152,6 +155,111 @@ class SetSessionDescriptionRequest WebKit::WebRTCVoidRequest webkit_request_; }; +// Class mapping responses from calls to libjingle +// GetStats into a WebKit::WebRTCStatsCallback. +class StatsResponse : public webrtc::StatsObserver { + public: + explicit StatsResponse(const scoped_refptr<LocalRTCStatsRequest>& request) + : request_(request), + response_(request_->createResponse()) {} + + virtual void OnComplete(const std::vector<webrtc::StatsReport>& reports) { + for (std::vector<webrtc::StatsReport>::const_iterator it = reports.begin(); + it != reports.end(); ++it) { + int idx = response_->addReport(); + if (it->local.values.size() > 0) { + AddElement(idx, true, it->id, it->type, it->local); + } + if (it->remote.values.size() > 0) { + AddElement(idx, false, it->id, it->type, it->remote); + } + } + request_->requestSucceeded(response_); + } + + private: + void AddElement(int idx, + bool is_local, + const std::string& id, + const std::string& type, + const webrtc::StatsElement& element) { + response_->addElement(idx, is_local, element.timestamp); + // TODO(hta): Make a better disposition of id and type. + AddStatistic(idx, is_local, "id", id); + AddStatistic(idx, is_local, "type", type); + for (webrtc::StatsElement::Values::const_iterator value_it = + element.values.begin(); + value_it != element.values.end(); ++value_it) { + AddStatistic(idx, is_local, value_it->name, value_it->value); + } + } + + void AddStatistic(int idx, bool is_local, const std::string& name, + const std::string& value) { + response_->addStatistic(idx, is_local, + WebKit::WebString::fromUTF8(name), + WebKit::WebString::fromUTF8(value)); + } + + talk_base::scoped_refptr<LocalRTCStatsRequest> request_; + talk_base::scoped_refptr<LocalRTCStatsResponse> response_; +}; + +// Implementation of LocalRTCStatsRequest +LocalRTCStatsRequest::LocalRTCStatsRequest(WebKit::WebRTCStatsRequest impl) + : impl_(impl), + response_(NULL) { +} + +LocalRTCStatsRequest::LocalRTCStatsRequest() {} +LocalRTCStatsRequest::~LocalRTCStatsRequest() {} + +bool LocalRTCStatsRequest::hasSelector() const { + return impl_.hasSelector(); +} + +WebKit::WebMediaStreamDescriptor LocalRTCStatsRequest::stream() const { + return impl_.stream(); +} + +WebKit::WebMediaStreamComponent LocalRTCStatsRequest::component() const { + return impl_.component(); +} + +scoped_refptr<LocalRTCStatsResponse> LocalRTCStatsRequest::createResponse() { + DCHECK(!response_); + response_ = new talk_base::RefCountedObject<LocalRTCStatsResponse>( + impl_.createResponse()); + return response_.get(); +} + +void LocalRTCStatsRequest::requestSucceeded( + const LocalRTCStatsResponse* response) { + impl_.requestSucceeded(response->webKitStatsResponse()); +} + +// Implementation of LocalRTCStatsResponse +WebKit::WebRTCStatsResponse LocalRTCStatsResponse::webKitStatsResponse() const { + return impl_; +} + +size_t LocalRTCStatsResponse::addReport() { + return impl_.addReport(); +} + +void LocalRTCStatsResponse::addElement(size_t report, + bool is_local, + double timestamp) { + impl_.addElement(report, is_local, timestamp); +} + +void LocalRTCStatsResponse::addStatistic(size_t report, + bool is_local, + WebKit::WebString name, + WebKit::WebString value) { + impl_.addStatistic(report, is_local, name, value); +} + RTCPeerConnectionHandler::RTCPeerConnectionHandler( WebKit::WebRTCPeerConnectionHandlerClient* client, MediaStreamDependencyFactory* dependency_factory) @@ -315,6 +423,46 @@ void RTCPeerConnectionHandler::removeStream( RemoveStream(stream); } +void RTCPeerConnectionHandler::getStats( + const WebKit::WebRTCStatsRequest& request) { + scoped_refptr<LocalRTCStatsRequest> inner_request( + new talk_base::RefCountedObject<LocalRTCStatsRequest>(request)); + getStats(inner_request); +} + +void RTCPeerConnectionHandler::getStats(LocalRTCStatsRequest* request) { + talk_base::scoped_refptr<webrtc::StatsObserver> observer( + new talk_base::RefCountedObject<StatsResponse>(request)); + webrtc::MediaStreamTrackInterface* track = NULL; + if (!native_peer_connection_) { + DVLOG(1) << "GetStats cannot verify selector on closed connection"; + // TODO(hta): Consider how to get an error back. + std::vector<webrtc::StatsReport> no_reports; + observer->OnComplete(no_reports); + return; + } + + if (request->hasSelector()) { + // Verify that stream is a member of local_streams + if (native_peer_connection_->local_streams() + ->find(UTF16ToUTF8(request->stream().label()))) { + track = GetLocalNativeMediaStreamTrack(request->stream(), + request->component()); + } else { + DVLOG(1) << "GetStats: Stream label not present in PC"; + } + + if (!track) { + DVLOG(1) << "GetStats: Track not found."; + // TODO(hta): Consider how to get an error back. + std::vector<webrtc::StatsReport> no_reports; + observer->OnComplete(no_reports); + return; + } + } + native_peer_connection_->GetStats(observer, track); +} + void RTCPeerConnectionHandler::stop() { DVLOG(1) << "RTCPeerConnectionHandler::stop"; native_peer_connection_ = NULL; diff --git a/content/renderer/media/rtc_peer_connection_handler.h b/content/renderer/media/rtc_peer_connection_handler.h index 01db449..dd9fee1 100644 --- a/content/renderer/media/rtc_peer_connection_handler.h +++ b/content/renderer/media/rtc_peer_connection_handler.h @@ -10,6 +10,8 @@ #include "content/common/content_export.h" #include "content/renderer/media/peer_connection_handler_base.h" #include "third_party/WebKit/Source/Platform/chromium/public/WebRTCPeerConnectionHandler.h" +#include "third_party/WebKit/Source/Platform/chromium/public/WebRTCStatsRequest.h" +#include "third_party/WebKit/Source/Platform/chromium/public/WebRTCStatsResponse.h" namespace WebKit { class WebFrame; @@ -17,10 +19,55 @@ class WebFrame; namespace content { +// Mockable wrapper for WebKit::WebRTCStatsResponse +class CONTENT_EXPORT LocalRTCStatsResponse + : public talk_base::RefCountInterface { + public: + explicit LocalRTCStatsResponse(const WebKit::WebRTCStatsResponse& impl) + : impl_(impl) { + } + // Constructor for testing. + LocalRTCStatsResponse() {} + + virtual WebKit::WebRTCStatsResponse webKitStatsResponse() const; + virtual size_t addReport(); + virtual void addElement(size_t report, bool is_local, double timestamp); + virtual void addStatistic(size_t report, bool is_local, + WebKit::WebString name, WebKit::WebString value); + + protected: + virtual ~LocalRTCStatsResponse() {} + + private: + WebKit::WebRTCStatsResponse impl_; +}; + +// Mockable wrapper for WebKit::WebRTCStatsRequest +class CONTENT_EXPORT LocalRTCStatsRequest + : public talk_base::RefCountInterface { + public: + explicit LocalRTCStatsRequest(WebKit::WebRTCStatsRequest impl); + // Constructor for testing. + LocalRTCStatsRequest(); + + virtual bool hasSelector() const; + virtual WebKit::WebMediaStreamDescriptor stream() const; + virtual WebKit::WebMediaStreamComponent component() const; + virtual void requestSucceeded(const LocalRTCStatsResponse* response); + virtual scoped_refptr<LocalRTCStatsResponse> createResponse(); + + protected: + virtual ~LocalRTCStatsRequest(); + + private: + WebKit::WebRTCStatsRequest impl_; + talk_base::scoped_refptr<LocalRTCStatsResponse> response_; +}; + // RTCPeerConnectionHandler is a delegate for the RTC PeerConnection API // messages going between WebKit and native PeerConnection in libjingle. It's // owned by WebKit. -// WebKit call all of these methods on the main render thread. +// WebKit calls all of these methods on the main render thread. // Callbacks to the webrtc::PeerConnectionObserver implementation also occur on // the main render thread. class CONTENT_EXPORT RTCPeerConnectionHandler @@ -73,6 +120,8 @@ class CONTENT_EXPORT RTCPeerConnectionHandler const WebKit::WebMediaConstraints& options) OVERRIDE; virtual void removeStream( const WebKit::WebMediaStreamDescriptor& stream) OVERRIDE; + virtual void getStats( + const WebKit::WebRTCStatsRequest& request) OVERRIDE; // We will be deleted by WebKit after stop has been returned. virtual void stop() OVERRIDE; @@ -86,6 +135,10 @@ class CONTENT_EXPORT RTCPeerConnectionHandler virtual void OnIceComplete() OVERRIDE; virtual void OnRenegotiationNeeded() OVERRIDE; + // Delegate functions to allow for mocking of WebKit interfaces. + // getStats takes ownership of request parameter. + virtual void getStats(LocalRTCStatsRequest* request); + private: webrtc::SessionDescriptionInterface* CreateNativeSessionDescription( const WebKit::WebRTCSessionDescription& description); diff --git a/content/renderer/media/rtc_peer_connection_handler_unittest.cc b/content/renderer/media/rtc_peer_connection_handler_unittest.cc index 71c6a3b..3ac8de1 100644 --- a/content/renderer/media/rtc_peer_connection_handler_unittest.cc +++ b/content/renderer/media/rtc_peer_connection_handler_unittest.cc @@ -14,6 +14,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "third_party/libjingle/source/talk/app/webrtc/peerconnectioninterface.h" #include "third_party/WebKit/Source/Platform/chromium/public/WebMediaConstraints.h" +#include "third_party/WebKit/Source/Platform/chromium/public/WebMediaStreamComponent.h" #include "third_party/WebKit/Source/Platform/chromium/public/WebMediaStreamDescriptor.h" #include "third_party/WebKit/Source/Platform/chromium/public/WebMediaStreamSource.h" #include "third_party/WebKit/Source/Platform/chromium/public/WebRTCConfiguration.h" @@ -21,6 +22,7 @@ #include "third_party/WebKit/Source/Platform/chromium/public/WebRTCPeerConnectionHandlerClient.h" #include "third_party/WebKit/Source/Platform/chromium/public/WebRTCSessionDescription.h" #include "third_party/WebKit/Source/Platform/chromium/public/WebRTCSessionDescriptionRequest.h" +#include "third_party/WebKit/Source/Platform/chromium/public/WebRTCStatsRequest.h" #include "third_party/WebKit/Source/Platform/chromium/public/WebRTCVoidRequest.h" #include "third_party/WebKit/Source/Platform/chromium/public/WebURL.h" @@ -29,6 +31,90 @@ static const char kDummySdpType[] = "dummy type"; namespace content { +class MockRTCStatsResponse : public LocalRTCStatsResponse { + public: + MockRTCStatsResponse() + : report_count_(0), + element_count_(0), + statistic_count_(0) { + } + + virtual size_t addReport() OVERRIDE { + ++report_count_; + return report_count_; + } + + virtual void addElement(size_t report, bool isLocal, double timestamp) + OVERRIDE { + ++element_count_; + } + + virtual void addStatistic(size_t report, bool isLocal, + WebKit::WebString name, WebKit::WebString value) + OVERRIDE { + ++statistic_count_; + } + int report_count() const { return report_count_; } + + private: + int report_count_; + int element_count_; + int statistic_count_; +}; + +// Mocked wrapper for WebKit::WebRTCStatsRequest +class MockRTCStatsRequest : public LocalRTCStatsRequest { + public: + MockRTCStatsRequest() + : has_selector_(false), + stream_(), + request_succeeded_called_(false) {} + + virtual bool hasSelector() const OVERRIDE { + return has_selector_; + } + virtual WebKit::WebMediaStreamDescriptor stream() const OVERRIDE { + return stream_; + } + virtual WebKit::WebMediaStreamComponent component() const OVERRIDE { + return component_; + } + virtual scoped_refptr<LocalRTCStatsResponse> createResponse() OVERRIDE { + DCHECK(!response_); + response_ = new talk_base::RefCountedObject<MockRTCStatsResponse>(); + return response_; + } + + virtual void requestSucceeded(const LocalRTCStatsResponse* response) + OVERRIDE { + EXPECT_EQ(response, response_.get()); + request_succeeded_called_ = true; + } + + // Function for setting whether or not a selector is available. + void setSelector(const WebKit::WebMediaStreamDescriptor& stream, + const WebKit::WebMediaStreamComponent& component) { + has_selector_ = true; + stream_ = stream; + component_ = component; + } + + // Function for inspecting the result of a stats request. + MockRTCStatsResponse* result() { + if (request_succeeded_called_) { + return response_.get(); + } else { + return NULL; + } + } + private: + bool has_selector_; + WebKit::WebMediaStreamDescriptor stream_; + WebKit::WebMediaStreamComponent component_; + scoped_refptr<MockRTCStatsResponse> response_; + bool request_succeeded_called_; +}; + class RTCPeerConnectionHandlerUnderTest : public RTCPeerConnectionHandler { public: RTCPeerConnectionHandlerUnderTest( @@ -82,7 +168,7 @@ class RTCPeerConnectionHandlerTest : public ::testing::Test { WebKit::WebVector<WebKit::WebMediaStreamSource> audio_sources( static_cast<size_t>(1)); - audio_sources[0].initialize(WebKit::WebString::fromUTF8(video_track_label), + audio_sources[0].initialize(WebKit::WebString::fromUTF8(audio_track_label), WebKit::WebMediaStreamSource::TypeAudio, WebKit::WebString::fromUTF8("audio_track")); WebKit::WebVector<WebKit::WebMediaStreamSource> video_sources( @@ -142,7 +228,7 @@ TEST_F(RTCPeerConnectionHandlerTest, CreateOffer) { EXPECT_TRUE(mock_peer_connection_->created_session_description() != NULL); } -TEST_F(RTCPeerConnectionHandlerTest, CreateAnser) { +TEST_F(RTCPeerConnectionHandlerTest, CreateAnswer) { WebKit::WebRTCSessionDescriptionRequest request; WebKit::WebMediaConstraints options; // TODO(perkj): Can WebKit::WebRTCSessionDescriptionRequest be changed so @@ -219,6 +305,62 @@ TEST_F(RTCPeerConnectionHandlerTest, addAndRemoveStream) { EXPECT_EQ(0u, mock_peer_connection_->local_streams()->count()); } +TEST_F(RTCPeerConnectionHandlerTest, GetStatsNoSelector) { + scoped_refptr<MockRTCStatsRequest> request( + new talk_base::RefCountedObject<MockRTCStatsRequest>()); + pc_handler_->getStats(request.get()); + // Note that callback gets executed synchronously by mock. + ASSERT_TRUE(request->result()); + EXPECT_LT(1, request->result()->report_count()); +} + +TEST_F(RTCPeerConnectionHandlerTest, GetStatsWithSelector) { + std::string stream_label = "local_stream"; + WebKit::WebMediaStreamDescriptor local_stream( + CreateLocalMediaStream(stream_label)); + WebKit::WebMediaConstraints constraints; + pc_handler_->addStream(local_stream, constraints); + WebKit::WebVector<WebKit::WebMediaStreamComponent> components; + local_stream.audioSources(components); + ASSERT_LE(1ul, components.size()); + + scoped_refptr<MockRTCStatsRequest> request( + new talk_base::RefCountedObject<MockRTCStatsRequest>()); + request->setSelector(local_stream, components[0]); + pc_handler_->getStats(request.get()); + EXPECT_EQ(1, request->result()->report_count()); +} + +TEST_F(RTCPeerConnectionHandlerTest, GetStatsAfterStop) { + scoped_refptr<MockRTCStatsRequest> request( + new talk_base::RefCountedObject<MockRTCStatsRequest>()); + pc_handler_->stop(); + pc_handler_->getStats(request.get()); + // Note that callback gets executed synchronously by mock. + ASSERT_TRUE(request->result()); + // Note - returning no stats is a temporary workaround. + EXPECT_EQ(0, request->result()->report_count()); +} + +TEST_F(RTCPeerConnectionHandlerTest, GetStatsWithBadSelector) { + // The setup is the same as above, but the stream is not added to the + // PeerConnection. + std::string stream_label = "local_stream_2"; + WebKit::WebMediaStreamDescriptor local_stream( + CreateLocalMediaStream(stream_label)); + WebKit::WebMediaConstraints constraints; + WebKit::WebVector<WebKit::WebMediaStreamComponent> components; + local_stream.audioSources(components); + WebKit::WebMediaStreamComponent component = components[0]; + EXPECT_EQ(0u, mock_peer_connection_->local_streams()->count()); + + scoped_refptr<MockRTCStatsRequest> request( + new talk_base::RefCountedObject<MockRTCStatsRequest>()); + request->setSelector(local_stream, component); + pc_handler_->getStats(request.get()); + EXPECT_EQ(0, request->result()->report_count()); +} + TEST_F(RTCPeerConnectionHandlerTest, OnStateChange) { // Ready states. webrtc::PeerConnectionObserver::StateType state = |