| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler.h" |
| |
| #include <stddef.h> |
| #include <string.h> |
| |
| #include <map> |
| #include <memory> |
| #include <set> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/callback_helpers.h" |
| #include "base/location.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/run_loop.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/synchronization/waitable_event.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/values.h" |
| #include "build/build_config.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/public/platform/modules/mediastream/web_media_stream_track.h" |
| #include "third_party/blink/public/platform/modules/mediastream/web_platform_media_stream_source.h" |
| #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h" |
| #include "third_party/blink/public/platform/web_url.h" |
| #include "third_party/blink/public/web/web_heap.h" |
| #include "third_party/blink/renderer/modules/mediastream/media_stream_video_track.h" |
| #include "third_party/blink/renderer/modules/mediastream/mock_media_stream_video_source.h" |
| #include "third_party/blink/renderer/modules/mediastream/processed_local_audio_source.h" |
| #include "third_party/blink/renderer/modules/mediastream/testing_platform_support_with_mock_audio_capture_source.h" |
| #include "third_party/blink/renderer/modules/peerconnection/adapters/web_rtc_cross_thread_copier.h" |
| #include "third_party/blink/renderer/modules/peerconnection/mock_data_channel_impl.h" |
| #include "third_party/blink/renderer/modules/peerconnection/mock_peer_connection_dependency_factory.h" |
| #include "third_party/blink/renderer/modules/peerconnection/mock_peer_connection_impl.h" |
| #include "third_party/blink/renderer/modules/peerconnection/mock_rtc_peer_connection_handler_client.h" |
| #include "third_party/blink/renderer/modules/peerconnection/mock_rtc_peer_connection_handler_platform.h" |
| #include "third_party/blink/renderer/modules/peerconnection/peer_connection_tracker.h" |
| #include "third_party/blink/renderer/modules/peerconnection/testing/fake_resource_listener.h" |
| #include "third_party/blink/renderer/modules/webrtc/webrtc_audio_device_impl.h" |
| #include "third_party/blink/renderer/platform/mediastream/media_constraints.h" |
| #include "third_party/blink/renderer/platform/mediastream/media_stream_audio_source.h" |
| #include "third_party/blink/renderer/platform/mediastream/media_stream_audio_track.h" |
| #include "third_party/blink/renderer/platform/mediastream/media_stream_component.h" |
| #include "third_party/blink/renderer/platform/mediastream/media_stream_descriptor.h" |
| #include "third_party/blink/renderer/platform/peerconnection/rtc_dtmf_sender_handler.h" |
| #include "third_party/blink/renderer/platform/peerconnection/rtc_ice_candidate_platform.h" |
| #include "third_party/blink/renderer/platform/peerconnection/rtc_peer_connection_handler_client.h" |
| #include "third_party/blink/renderer/platform/peerconnection/rtc_rtp_receiver_platform.h" |
| #include "third_party/blink/renderer/platform/peerconnection/rtc_session_description_platform.h" |
| #include "third_party/blink/renderer/platform/peerconnection/rtc_stats.h" |
| #include "third_party/blink/renderer/platform/peerconnection/rtc_stats_request.h" |
| #include "third_party/blink/renderer/platform/peerconnection/rtc_void_request.h" |
| #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" |
| #include "third_party/blink/renderer/platform/wtf/functional.h" |
| #include "third_party/webrtc/api/data_channel_interface.h" |
| #include "third_party/webrtc/api/peer_connection_interface.h" |
| #include "third_party/webrtc/api/rtp_receiver_interface.h" |
| #include "third_party/webrtc/stats/test/rtc_test_stats.h" |
| |
| static const char kDummySdp[] = "dummy sdp"; |
| static const char kDummySdpType[] = "dummy type"; |
| |
| using testing::_; |
| using testing::Invoke; |
| using testing::NiceMock; |
| using testing::Ref; |
| using testing::SaveArg; |
| using testing::WithArg; |
| |
| namespace blink { |
| |
| // Action SaveArgPointeeMove<k>(pointer) saves the value pointed to by the k-th |
| // (0-based) argument of the mock function by moving it to *pointer. |
| ACTION_TEMPLATE(SaveArgPointeeMove, |
| HAS_1_TEMPLATE_PARAMS(int, k), |
| AND_1_VALUE_PARAMS(pointer)) { |
| *pointer = std::move(*testing::get<k>(args)); |
| } |
| |
| class MockRTCStatsResponse : public LocalRTCStatsResponse { |
| public: |
| MockRTCStatsResponse() : report_count_(0), statistic_count_(0) {} |
| |
| void addStats(const RTCLegacyStats& stats) override { |
| ++report_count_; |
| for (std::unique_ptr<RTCLegacyStatsMemberIterator> member(stats.Iterator()); |
| !member->IsEnd(); member->Next()) { |
| ++statistic_count_; |
| } |
| } |
| |
| int report_count() const { return report_count_; } |
| |
| private: |
| int report_count_; |
| int statistic_count_; |
| }; |
| |
| // Mocked wrapper for RTCStatsRequest |
| class MockRTCStatsRequest : public LocalRTCStatsRequest { |
| public: |
| MockRTCStatsRequest() |
| : has_selector_(false), request_succeeded_called_(false) {} |
| |
| bool hasSelector() const override { return has_selector_; } |
| MediaStreamComponent* component() const override { return component_; } |
| scoped_refptr<LocalRTCStatsResponse> createResponse() override { |
| DCHECK(!response_.get()); |
| response_ = new rtc::RefCountedObject<MockRTCStatsResponse>(); |
| return response_; |
| } |
| |
| 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(MediaStreamComponent* component) { |
| has_selector_ = true; |
| component_ = component; |
| } |
| |
| // Function for inspecting the result of a stats request. |
| MockRTCStatsResponse* result() { |
| if (request_succeeded_called_) { |
| return response_.get(); |
| } else { |
| return nullptr; |
| } |
| } |
| |
| private: |
| bool has_selector_; |
| Persistent<MediaStreamComponent> component_; |
| scoped_refptr<MockRTCStatsResponse> response_; |
| bool request_succeeded_called_; |
| }; |
| |
| class MockPeerConnectionTracker : public PeerConnectionTracker { |
| public: |
| MockPeerConnectionTracker() |
| : PeerConnectionTracker( |
| blink::scheduler::GetSingleThreadTaskRunnerForTesting()) {} |
| |
| MOCK_METHOD1(UnregisterPeerConnection, |
| void(RTCPeerConnectionHandler* pc_handler)); |
| // TODO(jiayl): add coverage for the following methods |
| MOCK_METHOD2(TrackCreateOffer, |
| void(RTCPeerConnectionHandler* pc_handler, |
| const MediaConstraints& constraints)); |
| MOCK_METHOD2(TrackCreateAnswer, |
| void(RTCPeerConnectionHandler* pc_handler, |
| const MediaConstraints& constraints)); |
| MOCK_METHOD4(TrackSetSessionDescription, |
| void(RTCPeerConnectionHandler* pc_handler, |
| const String& sdp, |
| const String& type, |
| Source source)); |
| MOCK_METHOD1(TrackSetSessionDescriptionImplicit, |
| void(RTCPeerConnectionHandler* pc_handler)); |
| MOCK_METHOD2( |
| TrackSetConfiguration, |
| void(RTCPeerConnectionHandler* pc_handler, |
| const webrtc::PeerConnectionInterface::RTCConfiguration& config)); |
| MOCK_METHOD4(TrackAddIceCandidate, |
| void(RTCPeerConnectionHandler* pc_handler, |
| RTCIceCandidatePlatform* candidate, |
| Source source, |
| bool succeeded)); |
| MOCK_METHOD4(TrackAddTransceiver, |
| void(RTCPeerConnectionHandler* pc_handler, |
| TransceiverUpdatedReason reason, |
| const RTCRtpTransceiverPlatform& transceiver, |
| size_t transceiver_index)); |
| MOCK_METHOD4(TrackModifyTransceiver, |
| void(RTCPeerConnectionHandler* pc_handler, |
| TransceiverUpdatedReason reason, |
| const RTCRtpTransceiverPlatform& transceiver, |
| size_t transceiver_index)); |
| MOCK_METHOD4(TrackRemoveTransceiver, |
| void(RTCPeerConnectionHandler* pc_handler, |
| TransceiverUpdatedReason reason, |
| const RTCRtpTransceiverPlatform& transceiver, |
| size_t transceiver_index)); |
| MOCK_METHOD1(TrackOnIceComplete, void(RTCPeerConnectionHandler* pc_handler)); |
| MOCK_METHOD3(TrackCreateDataChannel, |
| void(RTCPeerConnectionHandler* pc_handler, |
| const webrtc::DataChannelInterface* data_channel, |
| Source source)); |
| MOCK_METHOD1(TrackStop, void(RTCPeerConnectionHandler* pc_handler)); |
| MOCK_METHOD2(TrackSignalingStateChange, |
| void(RTCPeerConnectionHandler* pc_handler, |
| webrtc::PeerConnectionInterface::SignalingState state)); |
| MOCK_METHOD2(TrackIceConnectionStateChange, |
| void(RTCPeerConnectionHandler* pc_handler, |
| webrtc::PeerConnectionInterface::IceConnectionState state)); |
| MOCK_METHOD2( |
| TrackConnectionStateChange, |
| void(RTCPeerConnectionHandler* pc_handler, |
| webrtc::PeerConnectionInterface::PeerConnectionState state)); |
| MOCK_METHOD2(TrackIceGatheringStateChange, |
| void(RTCPeerConnectionHandler* pc_handler, |
| webrtc::PeerConnectionInterface::IceGatheringState state)); |
| MOCK_METHOD4(TrackSessionDescriptionCallback, |
| void(RTCPeerConnectionHandler* pc_handler, |
| Action action, |
| const String& type, |
| const String& value)); |
| MOCK_METHOD1(TrackOnRenegotiationNeeded, |
| void(RTCPeerConnectionHandler* pc_handler)); |
| }; |
| |
| class DummyRTCVoidRequest final : public RTCVoidRequest { |
| public: |
| ~DummyRTCVoidRequest() override {} |
| |
| bool was_called() const { return was_called_; } |
| |
| void RequestSucceeded() override { was_called_ = true; } |
| void RequestFailed(const webrtc::RTCError&) override { was_called_ = true; } |
| void Trace(Visitor* visitor) const override { |
| RTCVoidRequest::Trace(visitor); |
| } |
| |
| private: |
| bool was_called_ = false; |
| }; |
| |
| void OnStatsDelivered(std::unique_ptr<RTCStatsReportPlatform>* result, |
| scoped_refptr<base::SingleThreadTaskRunner> main_thread, |
| std::unique_ptr<RTCStatsReportPlatform> report) { |
| EXPECT_TRUE(main_thread->BelongsToCurrentThread()); |
| EXPECT_TRUE(report); |
| result->reset(report.release()); |
| } |
| |
| template <typename T> |
| std::vector<T> ToSequence(T value) { |
| std::vector<T> vec; |
| vec.push_back(value); |
| return vec; |
| } |
| |
| template <typename T> |
| void ExpectSequenceEquals(const Vector<T>& sequence, T value) { |
| EXPECT_EQ(sequence.size(), static_cast<size_t>(1)); |
| EXPECT_EQ(sequence[0], value); |
| } |
| |
| class RTCPeerConnectionHandlerUnderTest : public RTCPeerConnectionHandler { |
| public: |
| RTCPeerConnectionHandlerUnderTest( |
| RTCPeerConnectionHandlerClient* client, |
| blink::PeerConnectionDependencyFactory* dependency_factory, |
| bool force_encoded_audio_insertable_streams = false, |
| bool force_encoded_video_insertable_streams = false) |
| : RTCPeerConnectionHandler( |
| client, |
| dependency_factory, |
| blink::scheduler::GetSingleThreadTaskRunnerForTesting(), |
| force_encoded_audio_insertable_streams, |
| force_encoded_video_insertable_streams) {} |
| |
| blink::MockPeerConnectionImpl* native_peer_connection() { |
| return static_cast<blink::MockPeerConnectionImpl*>( |
| RTCPeerConnectionHandler::native_peer_connection()); |
| } |
| |
| webrtc::PeerConnectionObserver* observer() { |
| return native_peer_connection()->observer(); |
| } |
| |
| bool HasThermalUmaListner() const { return thermal_uma_listener(); } |
| }; |
| |
| class RTCPeerConnectionHandlerTest : public ::testing::Test { |
| public: |
| RTCPeerConnectionHandlerTest() : mock_peer_connection_(nullptr) {} |
| |
| void SetUp() override { |
| mock_client_.reset(new NiceMock<MockRTCPeerConnectionHandlerClient>()); |
| mock_dependency_factory_.reset( |
| new blink::MockPeerConnectionDependencyFactory()); |
| |
| pc_handler_ = CreateRTCPeerConnectionHandlerUnderTest(); |
| mock_tracker_.reset(new NiceMock<MockPeerConnectionTracker>()); |
| webrtc::PeerConnectionInterface::RTCConfiguration config; |
| config.sdp_semantics = webrtc::SdpSemantics::kPlanB; |
| MediaConstraints constraints; |
| DummyExceptionStateForTesting exception_state; |
| EXPECT_TRUE(pc_handler_->InitializeForTest(config, constraints, |
| mock_tracker_.get()->AsWeakPtr(), |
| exception_state)); |
| mock_peer_connection_ = pc_handler_->native_peer_connection(); |
| ASSERT_TRUE(mock_peer_connection_); |
| EXPECT_CALL(*mock_peer_connection_, Close()); |
| } |
| |
| void TearDown() override { |
| pc_handler_.reset(); |
| mock_tracker_.reset(); |
| mock_dependency_factory_.reset(); |
| mock_client_.reset(); |
| blink::WebHeap::CollectAllGarbageForTesting(); |
| } |
| |
| std::unique_ptr<RTCPeerConnectionHandlerUnderTest> |
| CreateRTCPeerConnectionHandlerUnderTest() { |
| return std::make_unique<RTCPeerConnectionHandlerUnderTest>( |
| mock_client_.get(), mock_dependency_factory_.get()); |
| } |
| |
| // Creates a WebKit local MediaStream. |
| MediaStreamDescriptor* CreateLocalMediaStream(const String& stream_label) { |
| String video_track_label("video-label"); |
| String audio_track_label("audio-label"); |
| auto* audio_source = MakeGarbageCollected<MediaStreamSource>( |
| audio_track_label, MediaStreamSource::kTypeAudio, |
| String::FromUTF8("audio_track"), false /* remote */); |
| auto processed_audio_source = std::make_unique<ProcessedLocalAudioSource>( |
| nullptr /* consumer_web_frame is N/A for non-browser tests */, |
| MediaStreamDevice(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE, |
| "mock_device_id", "Mock device", |
| media::AudioParameters::kAudioCDSampleRate, |
| media::CHANNEL_LAYOUT_STEREO, |
| media::AudioParameters::kAudioCDSampleRate / 100), |
| false /* disable_local_echo */, blink::AudioProcessingProperties(), |
| base::DoNothing(), |
| blink::scheduler::GetSingleThreadTaskRunnerForTesting()); |
| auto* processed_audio_source_ptr = processed_audio_source.get(); |
| processed_audio_source->SetAllowInvalidRenderFrameIdForTesting(true); |
| audio_source->SetPlatformSource(std::move(processed_audio_source)); |
| |
| auto* video_source = MakeGarbageCollected<MediaStreamSource>( |
| video_track_label, MediaStreamSource::kTypeVideo, |
| String::FromUTF8("video_track"), false /* remote */); |
| auto native_video_source = std::make_unique<MockMediaStreamVideoSource>(); |
| auto* native_video_source_ptr = native_video_source.get(); |
| video_source->SetPlatformSource(std::move(native_video_source)); |
| |
| HeapVector<Member<MediaStreamComponent>> audio_components( |
| static_cast<size_t>(1)); |
| audio_components[0] = MakeGarbageCollected<MediaStreamComponent>( |
| audio_source->Id(), audio_source); |
| EXPECT_CALL( |
| *webrtc_audio_device_platform_support_->mock_audio_capturer_source(), |
| Initialize(_, _)); |
| EXPECT_CALL( |
| *webrtc_audio_device_platform_support_->mock_audio_capturer_source(), |
| SetAutomaticGainControl(true)); |
| EXPECT_CALL( |
| *webrtc_audio_device_platform_support_->mock_audio_capturer_source(), |
| Start()); |
| EXPECT_CALL( |
| *webrtc_audio_device_platform_support_->mock_audio_capturer_source(), |
| Stop()); |
| CHECK(processed_audio_source_ptr->ConnectToTrack(audio_components[0])); |
| |
| HeapVector<Member<MediaStreamComponent>> video_components( |
| static_cast<size_t>(1)); |
| video_components[0] = *MediaStreamVideoTrack::CreateVideoTrack( |
| native_video_source_ptr, |
| MediaStreamVideoSource::ConstraintsOnceCallback(), true); |
| |
| auto* local_stream = MakeGarbageCollected<MediaStreamDescriptor>( |
| stream_label, audio_components, video_components); |
| return local_stream; |
| } |
| |
| // Creates a remote MediaStream and adds it to the mocked native |
| // peer connection. |
| rtc::scoped_refptr<webrtc::MediaStreamInterface> AddRemoteMockMediaStream( |
| const String& stream_label, |
| const String& video_track_label, |
| const String& audio_track_label) { |
| rtc::scoped_refptr<webrtc::MediaStreamInterface> stream( |
| mock_dependency_factory_->CreateLocalMediaStream(stream_label).get()); |
| if (!video_track_label.IsEmpty()) { |
| InvokeAddTrack( |
| stream, MockWebRtcVideoTrack::Create(video_track_label.Utf8()).get()); |
| } |
| if (!audio_track_label.IsEmpty()) { |
| InvokeAddTrack( |
| stream, MockWebRtcAudioTrack::Create(audio_track_label.Utf8()).get()); |
| } |
| mock_peer_connection_->AddRemoteStream(stream); |
| return stream; |
| } |
| |
| void StopAllTracks(MediaStreamDescriptor* descriptor) { |
| for (auto component : descriptor->AudioComponents()) |
| MediaStreamAudioTrack::From(component.Get())->Stop(); |
| |
| for (auto component : descriptor->VideoComponents()) { |
| MediaStreamVideoTrack::From(component.Get())->Stop(); |
| } |
| } |
| |
| bool AddStream(MediaStreamDescriptor* descriptor) { |
| size_t senders_size_before_add = senders_.size(); |
| for (auto component : descriptor->AudioComponents()) { |
| auto error_or_transceiver = pc_handler_->AddTrack( |
| component, MediaStreamDescriptorVector({descriptor})); |
| if (error_or_transceiver.ok()) { |
| DCHECK_EQ( |
| error_or_transceiver.value()->ImplementationType(), |
| RTCRtpTransceiverPlatformImplementationType::kPlanBSenderOnly); |
| auto sender = error_or_transceiver.value()->Sender(); |
| senders_.push_back(std::unique_ptr<blink::RTCRtpSenderImpl>( |
| static_cast<blink::RTCRtpSenderImpl*>(sender.release()))); |
| } |
| } |
| for (auto component : descriptor->VideoComponents()) { |
| auto error_or_transceiver = pc_handler_->AddTrack( |
| component, MediaStreamDescriptorVector({descriptor})); |
| if (error_or_transceiver.ok()) { |
| DCHECK_EQ( |
| error_or_transceiver.value()->ImplementationType(), |
| RTCRtpTransceiverPlatformImplementationType::kPlanBSenderOnly); |
| auto sender = error_or_transceiver.value()->Sender(); |
| senders_.push_back(std::unique_ptr<blink::RTCRtpSenderImpl>( |
| static_cast<blink::RTCRtpSenderImpl*>(sender.release()))); |
| } |
| } |
| return senders_size_before_add < senders_.size(); |
| } |
| |
| std::vector<std::unique_ptr<blink::RTCRtpSenderImpl>>::iterator |
| FindSenderForTrack(MediaStreamComponent* component) { |
| for (auto it = senders_.begin(); it != senders_.end(); ++it) { |
| if ((*it)->Track()->UniqueId() == component->UniqueId()) |
| return it; |
| } |
| return senders_.end(); |
| } |
| |
| bool RemoveStream(MediaStreamDescriptor* descriptor) { |
| size_t senders_size_before_remove = senders_.size(); |
| // TODO(hbos): With Unified Plan senders are not removed. |
| // https://crbug.com/799030 |
| for (auto component : descriptor->AudioComponents()) { |
| auto it = FindSenderForTrack(component); |
| if (it != senders_.end() && pc_handler_->RemoveTrack((*it).get()).ok()) |
| senders_.erase(it); |
| } |
| for (auto component : descriptor->VideoComponents()) { |
| auto it = FindSenderForTrack(component); |
| if (it != senders_.end() && pc_handler_->RemoveTrack((*it).get()).ok()) |
| senders_.erase(it); |
| } |
| return senders_size_before_remove > senders_.size(); |
| } |
| |
| void InvokeOnAddStream( |
| const rtc::scoped_refptr<webrtc::MediaStreamInterface>& remote_stream) { |
| for (const auto& audio_track : remote_stream->GetAudioTracks()) { |
| InvokeOnAddTrack(audio_track, remote_stream); |
| } |
| for (const auto& video_track : remote_stream->GetVideoTracks()) { |
| InvokeOnAddTrack(video_track, remote_stream); |
| } |
| } |
| |
| void InvokeOnAddTrack( |
| const rtc::scoped_refptr<webrtc::MediaStreamTrackInterface>& remote_track, |
| const rtc::scoped_refptr<webrtc::MediaStreamInterface>& remote_stream) { |
| rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver( |
| new rtc::RefCountedObject<blink::FakeRtpReceiver>(remote_track)); |
| receivers_by_track_.insert(std::make_pair(remote_track.get(), receiver)); |
| std::vector<rtc::scoped_refptr<webrtc::MediaStreamInterface>> |
| receiver_streams; |
| receiver_streams.push_back(remote_stream); |
| InvokeOnSignalingThread( |
| CrossThreadBindOnce(&webrtc::PeerConnectionObserver::OnAddTrack, |
| CrossThreadUnretained(pc_handler_->observer()), |
| receiver, receiver_streams)); |
| } |
| |
| void InvokeOnRemoveStream( |
| const rtc::scoped_refptr<webrtc::MediaStreamInterface>& remote_stream) { |
| for (const auto& audio_track : remote_stream->GetAudioTracks()) { |
| InvokeOnRemoveTrack(audio_track); |
| } |
| for (const auto& video_track : remote_stream->GetVideoTracks()) { |
| InvokeOnRemoveTrack(video_track); |
| } |
| } |
| |
| void InvokeOnRemoveTrack( |
| const rtc::scoped_refptr<webrtc::MediaStreamTrackInterface>& |
| remote_track) { |
| rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver = |
| receivers_by_track_.find(remote_track.get())->second; |
| InvokeOnSignalingThread(CrossThreadBindOnce( |
| &webrtc::PeerConnectionObserver::OnRemoveTrack, |
| CrossThreadUnretained(pc_handler_->observer()), receiver)); |
| } |
| |
| template <typename T> |
| void InvokeAddTrack( |
| const rtc::scoped_refptr<webrtc::MediaStreamInterface>& remote_stream, |
| T* webrtc_track) { |
| InvokeOnSignalingThread(CrossThreadBindOnce( |
| [](webrtc::MediaStreamInterface* remote_stream, T* webrtc_track) { |
| EXPECT_TRUE(remote_stream->AddTrack(webrtc_track)); |
| }, |
| CrossThreadUnretained(remote_stream.get()), |
| CrossThreadUnretained(webrtc_track))); |
| } |
| |
| template <typename T> |
| void InvokeRemoveTrack( |
| const rtc::scoped_refptr<webrtc::MediaStreamInterface>& remote_stream, |
| T* webrtc_track) { |
| InvokeOnSignalingThread(CrossThreadBindOnce( |
| [](webrtc::MediaStreamInterface* remote_stream, T* webrtc_track) { |
| EXPECT_TRUE(remote_stream->RemoveTrack(webrtc_track)); |
| }, |
| CrossThreadUnretained(remote_stream.get()), |
| CrossThreadUnretained(webrtc_track))); |
| } |
| |
| bool HasReceiverForEveryTrack( |
| const rtc::scoped_refptr<webrtc::MediaStreamInterface>& remote_stream, |
| const std::vector<std::unique_ptr<RTCRtpReceiverPlatform>>& receivers) { |
| for (const auto& audio_track : remote_stream->GetAudioTracks()) { |
| if (!HasReceiverForTrack(*audio_track.get(), receivers)) |
| return false; |
| } |
| for (const auto& video_track : remote_stream->GetAudioTracks()) { |
| if (!HasReceiverForTrack(*video_track.get(), receivers)) |
| return false; |
| } |
| return true; |
| } |
| |
| bool HasReceiverForTrack( |
| const webrtc::MediaStreamTrackInterface& track, |
| const std::vector<std::unique_ptr<RTCRtpReceiverPlatform>>& receivers) { |
| for (const auto& receiver : receivers) { |
| if (receiver->Track()->Id().Utf8() == track.id()) |
| return true; |
| } |
| return false; |
| } |
| |
| void InvokeOnSignalingThread(WTF::CrossThreadOnceFunction<void()> callback) { |
| mock_dependency_factory_->GetWebRtcSignalingTaskRunner()->PostTask( |
| FROM_HERE, ConvertToBaseOnceCallback(std::move(callback))); |
| RunMessageLoopsUntilIdle(); |
| } |
| |
| // Wait for all current posts to the webrtc signaling thread to run and then |
| // run the message loop until idle on the main thread. |
| void RunMessageLoopsUntilIdle() { |
| base::WaitableEvent waitable_event( |
| base::WaitableEvent::ResetPolicy::MANUAL, |
| base::WaitableEvent::InitialState::NOT_SIGNALED); |
| mock_dependency_factory_->GetWebRtcSignalingTaskRunner()->PostTask( |
| FROM_HERE, base::BindOnce(&base::WaitableEvent::Signal, |
| base::Unretained(&waitable_event))); |
| waitable_event.Wait(); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| private: |
| void SignalWaitableEvent(base::WaitableEvent* waitable_event) { |
| waitable_event->Signal(); |
| } |
| |
| public: |
| ScopedTestingPlatformSupport<AudioCapturerSourceTestingPlatformSupport> |
| webrtc_audio_device_platform_support_; |
| std::unique_ptr<MockRTCPeerConnectionHandlerClient> mock_client_; |
| std::unique_ptr<blink::MockPeerConnectionDependencyFactory> |
| mock_dependency_factory_; |
| std::unique_ptr<NiceMock<MockPeerConnectionTracker>> mock_tracker_; |
| std::unique_ptr<RTCPeerConnectionHandlerUnderTest> pc_handler_; |
| |
| // Weak reference to the mocked native peer connection implementation. |
| blink::MockPeerConnectionImpl* mock_peer_connection_; |
| |
| std::vector<std::unique_ptr<blink::RTCRtpSenderImpl>> senders_; |
| std::map<webrtc::MediaStreamTrackInterface*, |
| rtc::scoped_refptr<webrtc::RtpReceiverInterface>> |
| receivers_by_track_; |
| }; |
| |
| TEST_F(RTCPeerConnectionHandlerTest, Destruct) { |
| EXPECT_CALL(*mock_tracker_.get(), UnregisterPeerConnection(pc_handler_.get())) |
| .Times(1); |
| pc_handler_.reset(nullptr); |
| } |
| |
| TEST_F(RTCPeerConnectionHandlerTest, NoCallbacksToClientAfterStop) { |
| pc_handler_->Stop(); |
| |
| EXPECT_CALL(*mock_client_.get(), NegotiationNeeded()).Times(0); |
| pc_handler_->observer()->OnRenegotiationNeeded(); |
| |
| EXPECT_CALL(*mock_client_.get(), DidGenerateICECandidate(_)).Times(0); |
| std::unique_ptr<webrtc::IceCandidateInterface> native_candidate( |
| mock_dependency_factory_->CreateIceCandidate("sdpMid", 1, kDummySdp)); |
| pc_handler_->observer()->OnIceCandidate(native_candidate.get()); |
| |
| EXPECT_CALL(*mock_client_.get(), DidChangeIceGatheringState(_)).Times(0); |
| pc_handler_->observer()->OnIceGatheringChange( |
| webrtc::PeerConnectionInterface::kIceGatheringNew); |
| |
| EXPECT_CALL(*mock_client_.get(), DidChangeIceConnectionState(_)).Times(0); |
| pc_handler_->observer()->OnIceConnectionChange( |
| webrtc::PeerConnectionInterface::kIceConnectionDisconnected); |
| |
| EXPECT_CALL(*mock_client_.get(), DidModifyReceiversPlanBForMock(_, _, _)) |
| .Times(0); |
| rtc::scoped_refptr<webrtc::MediaStreamInterface> remote_stream( |
| AddRemoteMockMediaStream("remote_stream", "video", "audio")); |
| InvokeOnAddStream(remote_stream); |
| |
| EXPECT_CALL(*mock_client_.get(), DidModifyReceiversPlanBForMock(_, _, _)) |
| .Times(0); |
| InvokeOnRemoveStream(remote_stream); |
| |
| EXPECT_CALL(*mock_client_.get(), DidAddRemoteDataChannel(_)).Times(0); |
| webrtc::DataChannelInit config; |
| rtc::scoped_refptr<webrtc::DataChannelInterface> remote_data_channel( |
| new rtc::RefCountedObject<blink::MockDataChannel>("dummy", &config)); |
| pc_handler_->observer()->OnDataChannel(remote_data_channel); |
| |
| RunMessageLoopsUntilIdle(); |
| } |
| |
| TEST_F(RTCPeerConnectionHandlerTest, CreateOffer) { |
| MediaConstraints options; |
| EXPECT_CALL(*mock_tracker_.get(), TrackCreateOffer(pc_handler_.get(), _)); |
| |
| // TODO(perkj): Can blink::RTCSessionDescriptionRequest be changed so |
| // the |request| requestSucceeded can be tested? Currently the |request| |
| // object can not be initialized from a unit test. |
| EXPECT_FALSE(mock_peer_connection_->created_session_description()); |
| pc_handler_->CreateOffer(nullptr /*RTCSessionDescriptionRequest*/, options); |
| EXPECT_TRUE(mock_peer_connection_->created_session_description()); |
| } |
| |
| TEST_F(RTCPeerConnectionHandlerTest, CreateAnswer) { |
| MediaConstraints options; |
| EXPECT_CALL(*mock_tracker_.get(), TrackCreateAnswer(pc_handler_.get(), _)); |
| // TODO(perkj): Can blink::RTCSessionDescriptionRequest be changed so |
| // the |request| requestSucceeded can be tested? Currently the |request| |
| // object can not be initialized from a unit test. |
| EXPECT_FALSE(mock_peer_connection_->created_session_description()); |
| pc_handler_->CreateAnswer(nullptr /*RTCSessionDescriptionRequest*/, options); |
| EXPECT_TRUE(mock_peer_connection_->created_session_description()); |
| } |
| |
| TEST_F(RTCPeerConnectionHandlerTest, setLocalDescription) { |
| // PeerConnectionTracker::TrackSetSessionDescription is expected to be called |
| // before |mock_peer_connection| is called. |
| testing::InSequence sequence; |
| EXPECT_CALL(*mock_tracker_.get(), |
| TrackSetSessionDescription(pc_handler_.get(), String(kDummySdp), |
| String(kDummySdpType), |
| PeerConnectionTracker::SOURCE_LOCAL)); |
| EXPECT_CALL(*mock_peer_connection_, SetLocalDescriptionForMock(_, _)); |
| |
| pc_handler_->SetLocalDescription( |
| nullptr /*RTCVoidRequest*/, |
| MockParsedSessionDescription(kDummySdpType, kDummySdp)); |
| RunMessageLoopsUntilIdle(); |
| |
| std::string sdp_string; |
| ASSERT_TRUE(mock_peer_connection_->local_description()); |
| EXPECT_EQ(kDummySdpType, mock_peer_connection_->local_description()->type()); |
| mock_peer_connection_->local_description()->ToString(&sdp_string); |
| EXPECT_EQ(kDummySdp, sdp_string); |
| |
| // TODO(deadbeef): Also mock the "success" callback from the PeerConnection |
| // and ensure that the sucessful result is tracked by PeerConnectionTracker. |
| } |
| |
| // Test that setLocalDescription with invalid SDP will result in a failure, and |
| // is tracked as a failure with PeerConnectionTracker. |
| TEST_F(RTCPeerConnectionHandlerTest, setLocalDescriptionParseError) { |
| auto* description = MakeGarbageCollected<RTCSessionDescriptionPlatform>( |
| kDummySdpType, kDummySdp); |
| testing::InSequence sequence; |
| // Expect two "Track" calls, one for the start of the attempt and one for the |
| // failure. |
| EXPECT_CALL(*mock_tracker_.get(), |
| TrackSetSessionDescription(pc_handler_.get(), String(kDummySdp), |
| String(kDummySdpType), |
| PeerConnectionTracker::SOURCE_LOCAL)); |
| EXPECT_CALL(*mock_tracker_.get(), |
| TrackSessionDescriptionCallback( |
| pc_handler_.get(), |
| PeerConnectionTracker::ACTION_SET_LOCAL_DESCRIPTION, |
| String("OnFailure"), _)); |
| |
| // Used to simulate a parse failure. |
| mock_dependency_factory_->SetFailToCreateSessionDescription(true); |
| pc_handler_->SetLocalDescription( |
| nullptr /*RTCVoidRequest*/, ParsedSessionDescription::Parse(description)); |
| RunMessageLoopsUntilIdle(); |
| } |
| |
| TEST_F(RTCPeerConnectionHandlerTest, setRemoteDescription) { |
| // PeerConnectionTracker::TrackSetSessionDescription is expected to be called |
| // before |mock_peer_connection| is called. |
| testing::InSequence sequence; |
| EXPECT_CALL(*mock_tracker_.get(), |
| TrackSetSessionDescription(pc_handler_.get(), String(kDummySdp), |
| String(kDummySdpType), |
| PeerConnectionTracker::SOURCE_REMOTE)); |
| EXPECT_CALL(*mock_peer_connection_, SetRemoteDescriptionForMock(_, _)); |
| |
| pc_handler_->SetRemoteDescription( |
| nullptr /*RTCVoidRequest*/, |
| MockParsedSessionDescription(kDummySdpType, kDummySdp)); |
| RunMessageLoopsUntilIdle(); |
| |
| std::string sdp_string; |
| ASSERT_TRUE(mock_peer_connection_->remote_description()); |
| EXPECT_EQ(kDummySdpType, mock_peer_connection_->remote_description()->type()); |
| mock_peer_connection_->remote_description()->ToString(&sdp_string); |
| EXPECT_EQ(kDummySdp, sdp_string); |
| |
| // TODO(deadbeef): Also mock the "success" callback from the PeerConnection |
| // and ensure that the sucessful result is tracked by PeerConnectionTracker. |
| } |
| |
| // Test that setRemoteDescription with invalid SDP will result in a failure, and |
| // is tracked as a failure with PeerConnectionTracker. |
| TEST_F(RTCPeerConnectionHandlerTest, setRemoteDescriptionParseError) { |
| auto* description = MakeGarbageCollected<RTCSessionDescriptionPlatform>( |
| kDummySdpType, kDummySdp); |
| testing::InSequence sequence; |
| // Expect two "Track" calls, one for the start of the attempt and one for the |
| // failure. |
| EXPECT_CALL(*mock_tracker_.get(), |
| TrackSetSessionDescription(pc_handler_.get(), String(kDummySdp), |
| String(kDummySdpType), |
| PeerConnectionTracker::SOURCE_REMOTE)); |
| EXPECT_CALL(*mock_tracker_.get(), |
| TrackSessionDescriptionCallback( |
| pc_handler_.get(), |
| PeerConnectionTracker::ACTION_SET_REMOTE_DESCRIPTION, |
| String("OnFailure"), _)); |
| |
| // Used to simulate a parse failure. |
| mock_dependency_factory_->SetFailToCreateSessionDescription(true); |
| pc_handler_->SetRemoteDescription( |
| nullptr /*RTCVoidRequest*/, ParsedSessionDescription::Parse(description)); |
| RunMessageLoopsUntilIdle(); |
| } |
| |
| TEST_F(RTCPeerConnectionHandlerTest, setConfiguration) { |
| webrtc::PeerConnectionInterface::RTCConfiguration config; |
| config.sdp_semantics = webrtc::SdpSemantics::kPlanB; |
| |
| EXPECT_CALL(*mock_tracker_.get(), |
| TrackSetConfiguration(pc_handler_.get(), _)); |
| EXPECT_EQ(webrtc::RTCErrorType::NONE, pc_handler_->SetConfiguration(config)); |
| } |
| |
| // Test that when an error occurs in SetConfiguration, it's converted to a |
| // blink error and false is returned. |
| TEST_F(RTCPeerConnectionHandlerTest, setConfigurationError) { |
| webrtc::PeerConnectionInterface::RTCConfiguration config; |
| config.sdp_semantics = webrtc::SdpSemantics::kPlanB; |
| |
| mock_peer_connection_->set_setconfiguration_error_type( |
| webrtc::RTCErrorType::INVALID_MODIFICATION); |
| EXPECT_CALL(*mock_tracker_.get(), |
| TrackSetConfiguration(pc_handler_.get(), _)); |
| EXPECT_EQ(webrtc::RTCErrorType::INVALID_MODIFICATION, |
| pc_handler_->SetConfiguration(config)); |
| } |
| |
| TEST_F(RTCPeerConnectionHandlerTest, addICECandidate) { |
| auto* candidate = |
| MakeGarbageCollected<RTCIceCandidatePlatform>(kDummySdp, "sdpMid", 1); |
| |
| EXPECT_CALL(*mock_tracker_.get(), |
| TrackAddIceCandidate(pc_handler_.get(), candidate, |
| PeerConnectionTracker::SOURCE_REMOTE, true)); |
| auto* request = MakeGarbageCollected<DummyRTCVoidRequest>(); |
| pc_handler_->AddICECandidate(request, candidate); |
| RunMessageLoopsUntilIdle(); |
| EXPECT_TRUE(request->was_called()); |
| EXPECT_EQ(kDummySdp, mock_peer_connection_->ice_sdp()); |
| EXPECT_EQ(1, mock_peer_connection_->sdp_mline_index()); |
| EXPECT_EQ("sdpMid", mock_peer_connection_->sdp_mid()); |
| } |
| |
| TEST_F(RTCPeerConnectionHandlerTest, addAndRemoveStream) { |
| String stream_label = "local_stream"; |
| MediaStreamDescriptor* local_stream = CreateLocalMediaStream(stream_label); |
| |
| EXPECT_CALL( |
| *mock_tracker_.get(), |
| TrackAddTransceiver( |
| pc_handler_.get(), |
| PeerConnectionTracker::TransceiverUpdatedReason::kAddTrack, _, _)) |
| .Times(2); |
| EXPECT_CALL( |
| *mock_tracker_.get(), |
| TrackRemoveTransceiver( |
| pc_handler_.get(), |
| PeerConnectionTracker::TransceiverUpdatedReason::kRemoveTrack, _, _)) |
| .Times(2); |
| EXPECT_TRUE(AddStream(local_stream)); |
| EXPECT_EQ(stream_label.Utf8(), mock_peer_connection_->stream_label()); |
| EXPECT_EQ(2u, mock_peer_connection_->GetSenders().size()); |
| |
| EXPECT_FALSE(AddStream(local_stream)); |
| EXPECT_TRUE(RemoveStream(local_stream)); |
| EXPECT_EQ(0u, mock_peer_connection_->GetSenders().size()); |
| |
| StopAllTracks(local_stream); |
| } |
| |
| TEST_F(RTCPeerConnectionHandlerTest, addStreamWithStoppedAudioAndVideoTrack) { |
| String stream_label = "local_stream"; |
| MediaStreamDescriptor* local_stream = CreateLocalMediaStream(stream_label); |
| |
| auto audio_components = local_stream->AudioComponents(); |
| auto* native_audio_source = |
| MediaStreamAudioSource::From(audio_components[0]->Source()); |
| native_audio_source->StopSource(); |
| |
| auto video_tracks = local_stream->VideoComponents(); |
| auto* native_video_source = static_cast<MediaStreamVideoSource*>( |
| video_tracks[0]->Source()->GetPlatformSource()); |
| native_video_source->StopSource(); |
| |
| EXPECT_TRUE(AddStream(local_stream)); |
| EXPECT_EQ(stream_label.Utf8(), mock_peer_connection_->stream_label()); |
| EXPECT_EQ(2u, mock_peer_connection_->GetSenders().size()); |
| |
| StopAllTracks(local_stream); |
| } |
| |
| TEST_F(RTCPeerConnectionHandlerTest, GetStatsNoSelector) { |
| scoped_refptr<MockRTCStatsRequest> request( |
| new rtc::RefCountedObject<MockRTCStatsRequest>()); |
| pc_handler_->getStats(request.get()); |
| RunMessageLoopsUntilIdle(); |
| ASSERT_TRUE(request->result()); |
| EXPECT_LT(1, request->result()->report_count()); |
| } |
| |
| TEST_F(RTCPeerConnectionHandlerTest, GetStatsAfterClose) { |
| scoped_refptr<MockRTCStatsRequest> request( |
| new rtc::RefCountedObject<MockRTCStatsRequest>()); |
| pc_handler_->Stop(); |
| RunMessageLoopsUntilIdle(); |
| pc_handler_->getStats(request.get()); |
| RunMessageLoopsUntilIdle(); |
| ASSERT_TRUE(request->result()); |
| EXPECT_LT(1, request->result()->report_count()); |
| } |
| |
| TEST_F(RTCPeerConnectionHandlerTest, GetStatsWithLocalSelector) { |
| MediaStreamDescriptor* local_stream = CreateLocalMediaStream("local_stream"); |
| EXPECT_TRUE(AddStream(local_stream)); |
| auto components = local_stream->AudioComponents(); |
| ASSERT_LE(1ul, components.size()); |
| |
| scoped_refptr<MockRTCStatsRequest> request( |
| new rtc::RefCountedObject<MockRTCStatsRequest>()); |
| request->setSelector(components[0]); |
| pc_handler_->getStats(request.get()); |
| RunMessageLoopsUntilIdle(); |
| EXPECT_EQ(1, request->result()->report_count()); |
| |
| StopAllTracks(local_stream); |
| } |
| |
| TEST_F(RTCPeerConnectionHandlerTest, GetStatsWithBadSelector) { |
| // The setup is the same as GetStatsWithLocalSelector, but the stream is not |
| // added to the PeerConnection. |
| MediaStreamDescriptor* local_stream = |
| CreateLocalMediaStream("local_stream_2"); |
| auto tracks = local_stream->AudioComponents(); |
| Member<MediaStreamComponent> component = tracks[0]; |
| mock_peer_connection_->SetGetStatsResult(false); |
| |
| scoped_refptr<MockRTCStatsRequest> request( |
| new rtc::RefCountedObject<MockRTCStatsRequest>()); |
| request->setSelector(component); |
| pc_handler_->getStats(request.get()); |
| RunMessageLoopsUntilIdle(); |
| EXPECT_EQ(0, request->result()->report_count()); |
| |
| StopAllTracks(local_stream); |
| } |
| |
| TEST_F(RTCPeerConnectionHandlerTest, GetRTCStats) { |
| blink::AllowStatsForTesting(webrtc::RTCTestStats::kType); |
| |
| rtc::scoped_refptr<webrtc::RTCStatsReport> report = |
| webrtc::RTCStatsReport::Create(); |
| |
| report->AddStats(std::unique_ptr<const webrtc::RTCStats>( |
| new webrtc::RTCTestStats("RTCUndefinedStats", 1000))); |
| |
| std::unique_ptr<webrtc::RTCTestStats> stats_defined_members( |
| new webrtc::RTCTestStats("RTCDefinedStats", 2000)); |
| stats_defined_members->m_bool = true; |
| stats_defined_members->m_int32 = 42; |
| stats_defined_members->m_uint32 = 42; |
| stats_defined_members->m_int64 = 42; |
| stats_defined_members->m_uint64 = 42; |
| stats_defined_members->m_double = 42.0; |
| stats_defined_members->m_string = "42"; |
| stats_defined_members->m_sequence_bool = ToSequence<bool>(true); |
| stats_defined_members->m_sequence_int32 = ToSequence<int32_t>(42); |
| stats_defined_members->m_sequence_uint32 = ToSequence<uint32_t>(42); |
| stats_defined_members->m_sequence_int64 = ToSequence<int64_t>(42); |
| stats_defined_members->m_sequence_uint64 = ToSequence<uint64_t>(42); |
| stats_defined_members->m_sequence_double = ToSequence<double>(42); |
| stats_defined_members->m_sequence_string = ToSequence<std::string>("42"); |
| report->AddStats( |
| std::unique_ptr<const webrtc::RTCStats>(stats_defined_members.release())); |
| |
| pc_handler_->native_peer_connection()->SetGetStatsReport(report); |
| std::unique_ptr<RTCStatsReportPlatform> result; |
| pc_handler_->GetStats( |
| base::BindOnce(OnStatsDelivered, &result, |
| blink::scheduler::GetSingleThreadTaskRunnerForTesting()), |
| {}); |
| RunMessageLoopsUntilIdle(); |
| EXPECT_TRUE(result); |
| |
| int undefined_stats_count = 0; |
| int defined_stats_count = 0; |
| for (std::unique_ptr<RTCStats> stats = result->Next(); stats; |
| stats.reset(result->Next().release())) { |
| EXPECT_EQ(stats->GetType().Utf8(), webrtc::RTCTestStats::kType); |
| if (stats->Id().Utf8() == "RTCUndefinedStats") { |
| ++undefined_stats_count; |
| EXPECT_EQ(stats->Timestamp(), 1.0); |
| for (size_t i = 0; i < stats->MembersCount(); ++i) { |
| EXPECT_FALSE(stats->GetMember(i)->IsDefined()); |
| } |
| } else if (stats->Id().Utf8() == "RTCDefinedStats") { |
| ++defined_stats_count; |
| EXPECT_EQ(stats->Timestamp(), 2.0); |
| std::set<webrtc::RTCStatsMemberInterface::Type> members; |
| for (size_t i = 0; i < stats->MembersCount(); ++i) { |
| std::unique_ptr<RTCStatsMember> member = stats->GetMember(i); |
| // TODO(hbos): A WebRTC-change is adding new members, this would cause |
| // not all members to be defined. This if-statement saves Chromium from |
| // crashing. As soon as the change has been rolled in, I will update |
| // this test. crbug.com/627816 |
| if (!member->IsDefined()) |
| continue; |
| EXPECT_TRUE(member->IsDefined()); |
| members.insert(member->GetType()); |
| switch (member->GetType()) { |
| case webrtc::RTCStatsMemberInterface::kBool: |
| EXPECT_EQ(member->ValueBool(), true); |
| break; |
| case webrtc::RTCStatsMemberInterface::kInt32: |
| EXPECT_EQ(member->ValueInt32(), static_cast<int32_t>(42)); |
| break; |
| case webrtc::RTCStatsMemberInterface::kUint32: |
| EXPECT_EQ(member->ValueUint32(), static_cast<uint32_t>(42)); |
| break; |
| case webrtc::RTCStatsMemberInterface::kInt64: |
| EXPECT_EQ(member->ValueInt64(), static_cast<int64_t>(42)); |
| break; |
| case webrtc::RTCStatsMemberInterface::kUint64: |
| EXPECT_EQ(member->ValueUint64(), static_cast<uint64_t>(42)); |
| break; |
| case webrtc::RTCStatsMemberInterface::kDouble: |
| EXPECT_EQ(member->ValueDouble(), 42.0); |
| break; |
| case webrtc::RTCStatsMemberInterface::kString: |
| EXPECT_EQ(member->ValueString(), "42"); |
| break; |
| case webrtc::RTCStatsMemberInterface::kSequenceBool: |
| ExpectSequenceEquals(member->ValueSequenceBool(), true); |
| break; |
| case webrtc::RTCStatsMemberInterface::kSequenceInt32: |
| ExpectSequenceEquals(member->ValueSequenceInt32(), |
| static_cast<int32_t>(42)); |
| break; |
| case webrtc::RTCStatsMemberInterface::kSequenceUint32: |
| ExpectSequenceEquals(member->ValueSequenceUint32(), |
| static_cast<uint32_t>(42)); |
| break; |
| case webrtc::RTCStatsMemberInterface::kSequenceInt64: |
| ExpectSequenceEquals(member->ValueSequenceInt64(), |
| static_cast<int64_t>(42)); |
| break; |
| case webrtc::RTCStatsMemberInterface::kSequenceUint64: |
| ExpectSequenceEquals(member->ValueSequenceUint64(), |
| static_cast<uint64_t>(42)); |
| break; |
| case webrtc::RTCStatsMemberInterface::kSequenceDouble: |
| ExpectSequenceEquals(member->ValueSequenceDouble(), 42.0); |
| break; |
| case webrtc::RTCStatsMemberInterface::kSequenceString: |
| ExpectSequenceEquals(member->ValueSequenceString(), |
| String::FromUTF8("42")); |
| break; |
| default: |
| NOTREACHED(); |
| } |
| } |
| EXPECT_EQ(members.size(), static_cast<size_t>(14)); |
| } else { |
| NOTREACHED(); |
| } |
| } |
| EXPECT_EQ(undefined_stats_count, 1); |
| EXPECT_EQ(defined_stats_count, 1); |
| } |
| |
| TEST_F(RTCPeerConnectionHandlerTest, OnIceConnectionChange) { |
| testing::InSequence sequence; |
| |
| webrtc::PeerConnectionInterface::IceConnectionState new_state = |
| webrtc::PeerConnectionInterface::kIceConnectionNew; |
| EXPECT_CALL(*mock_client_.get(), |
| DidChangeIceConnectionState( |
| webrtc::PeerConnectionInterface::kIceConnectionNew)); |
| pc_handler_->observer()->OnStandardizedIceConnectionChange(new_state); |
| |
| new_state = webrtc::PeerConnectionInterface::kIceConnectionChecking; |
| EXPECT_CALL(*mock_client_.get(), |
| DidChangeIceConnectionState( |
| webrtc::PeerConnectionInterface::kIceConnectionChecking)); |
| pc_handler_->observer()->OnStandardizedIceConnectionChange(new_state); |
| |
| new_state = webrtc::PeerConnectionInterface::kIceConnectionConnected; |
| EXPECT_CALL(*mock_client_.get(), |
| DidChangeIceConnectionState( |
| webrtc::PeerConnectionInterface::kIceConnectionConnected)); |
| pc_handler_->observer()->OnStandardizedIceConnectionChange(new_state); |
| |
| new_state = webrtc::PeerConnectionInterface::kIceConnectionCompleted; |
| EXPECT_CALL(*mock_client_.get(), |
| DidChangeIceConnectionState( |
| webrtc::PeerConnectionInterface::kIceConnectionCompleted)); |
| pc_handler_->observer()->OnStandardizedIceConnectionChange(new_state); |
| |
| new_state = webrtc::PeerConnectionInterface::kIceConnectionFailed; |
| EXPECT_CALL(*mock_client_.get(), |
| DidChangeIceConnectionState( |
| webrtc::PeerConnectionInterface::kIceConnectionFailed)); |
| pc_handler_->observer()->OnStandardizedIceConnectionChange(new_state); |
| |
| new_state = webrtc::PeerConnectionInterface::kIceConnectionDisconnected; |
| EXPECT_CALL(*mock_client_.get(), |
| DidChangeIceConnectionState( |
| webrtc::PeerConnectionInterface::kIceConnectionDisconnected)); |
| pc_handler_->observer()->OnStandardizedIceConnectionChange(new_state); |
| |
| new_state = webrtc::PeerConnectionInterface::kIceConnectionClosed; |
| EXPECT_CALL(*mock_client_.get(), |
| DidChangeIceConnectionState( |
| webrtc::PeerConnectionInterface::kIceConnectionClosed)); |
| pc_handler_->observer()->OnStandardizedIceConnectionChange(new_state); |
| } |
| |
| TEST_F(RTCPeerConnectionHandlerTest, OnConnectionChange) { |
| testing::InSequence sequence; |
| |
| webrtc::PeerConnectionInterface::PeerConnectionState new_state = |
| webrtc::PeerConnectionInterface::PeerConnectionState::kNew; |
| EXPECT_CALL(*mock_tracker_.get(), |
| TrackConnectionStateChange( |
| pc_handler_.get(), |
| webrtc::PeerConnectionInterface::PeerConnectionState::kNew)); |
| EXPECT_CALL(*mock_client_.get(), |
| DidChangePeerConnectionState( |
| webrtc::PeerConnectionInterface::PeerConnectionState::kNew)); |
| pc_handler_->observer()->OnConnectionChange(new_state); |
| |
| new_state = webrtc::PeerConnectionInterface::PeerConnectionState::kConnecting; |
| EXPECT_CALL( |
| *mock_tracker_.get(), |
| TrackConnectionStateChange( |
| pc_handler_.get(), |
| webrtc::PeerConnectionInterface::PeerConnectionState::kConnecting)); |
| EXPECT_CALL( |
| *mock_client_.get(), |
| DidChangePeerConnectionState( |
| webrtc::PeerConnectionInterface::PeerConnectionState::kConnecting)); |
| pc_handler_->observer()->OnConnectionChange(new_state); |
| |
| new_state = webrtc::PeerConnectionInterface::PeerConnectionState::kConnected; |
| EXPECT_CALL( |
| *mock_tracker_.get(), |
| TrackConnectionStateChange( |
| pc_handler_.get(), |
| webrtc::PeerConnectionInterface::PeerConnectionState::kConnected)); |
| EXPECT_CALL( |
| *mock_client_.get(), |
| DidChangePeerConnectionState( |
| webrtc::PeerConnectionInterface::PeerConnectionState::kConnected)); |
| pc_handler_->observer()->OnConnectionChange(new_state); |
| |
| new_state = |
| webrtc::PeerConnectionInterface::PeerConnectionState::kDisconnected; |
| EXPECT_CALL( |
| *mock_tracker_.get(), |
| TrackConnectionStateChange( |
| pc_handler_.get(), |
| webrtc::PeerConnectionInterface::PeerConnectionState::kDisconnected)); |
| EXPECT_CALL( |
| *mock_client_.get(), |
| DidChangePeerConnectionState( |
| webrtc::PeerConnectionInterface::PeerConnectionState::kDisconnected)); |
| pc_handler_->observer()->OnConnectionChange(new_state); |
| |
| new_state = webrtc::PeerConnectionInterface::PeerConnectionState::kFailed; |
| EXPECT_CALL( |
| *mock_tracker_.get(), |
| TrackConnectionStateChange( |
| pc_handler_.get(), |
| webrtc::PeerConnectionInterface::PeerConnectionState::kFailed)); |
| EXPECT_CALL( |
| *mock_client_.get(), |
| DidChangePeerConnectionState( |
| webrtc::PeerConnectionInterface::PeerConnectionState::kFailed)); |
| pc_handler_->observer()->OnConnectionChange(new_state); |
| |
| new_state = webrtc::PeerConnectionInterface::PeerConnectionState::kClosed; |
| EXPECT_CALL( |
| *mock_tracker_.get(), |
| TrackConnectionStateChange( |
| pc_handler_.get(), |
| webrtc::PeerConnectionInterface::PeerConnectionState::kClosed)); |
| EXPECT_CALL( |
| *mock_client_.get(), |
| DidChangePeerConnectionState( |
| webrtc::PeerConnectionInterface::PeerConnectionState::kClosed)); |
| pc_handler_->observer()->OnConnectionChange(new_state); |
| } |
| |
| TEST_F(RTCPeerConnectionHandlerTest, OnIceGatheringChange) { |
| testing::InSequence sequence; |
| EXPECT_CALL(*mock_tracker_.get(), |
| TrackIceGatheringStateChange( |
| pc_handler_.get(), |
| webrtc::PeerConnectionInterface::kIceGatheringNew)); |
| EXPECT_CALL(*mock_client_.get(), |
| DidChangeIceGatheringState( |
| webrtc::PeerConnectionInterface::kIceGatheringNew)); |
| EXPECT_CALL(*mock_tracker_.get(), |
| TrackIceGatheringStateChange( |
| pc_handler_.get(), |
| webrtc::PeerConnectionInterface::kIceGatheringGathering)); |
| EXPECT_CALL(*mock_client_.get(), |
| DidChangeIceGatheringState( |
| webrtc::PeerConnectionInterface::kIceGatheringGathering)); |
| EXPECT_CALL(*mock_tracker_.get(), |
| TrackIceGatheringStateChange( |
| pc_handler_.get(), |
| webrtc::PeerConnectionInterface::kIceGatheringComplete)); |
| EXPECT_CALL(*mock_client_.get(), |
| DidChangeIceGatheringState( |
| webrtc::PeerConnectionInterface::kIceGatheringComplete)); |
| |
| webrtc::PeerConnectionInterface::IceGatheringState new_state = |
| webrtc::PeerConnectionInterface::kIceGatheringNew; |
| pc_handler_->observer()->OnIceGatheringChange(new_state); |
| |
| new_state = webrtc::PeerConnectionInterface::kIceGatheringGathering; |
| pc_handler_->observer()->OnIceGatheringChange(new_state); |
| |
| new_state = webrtc::PeerConnectionInterface::kIceGatheringComplete; |
| pc_handler_->observer()->OnIceGatheringChange(new_state); |
| |
| // Check NULL candidate after ice gathering is completed. |
| EXPECT_EQ("", mock_client_->candidate_mid()); |
| EXPECT_FALSE(mock_client_->candidate_mlineindex().has_value()); |
| EXPECT_EQ("", mock_client_->candidate_sdp()); |
| } |
| |
| // TODO(hbos): Enable when not mocking or remove test. https://crbug.com/788659 |
| TEST_F(RTCPeerConnectionHandlerTest, DISABLED_OnAddAndOnRemoveStream) { |
| rtc::scoped_refptr<webrtc::MediaStreamInterface> remote_stream( |
| AddRemoteMockMediaStream("remote_stream", "video", "audio")); |
| // Grab receivers when they're added to/removed from the PC. |
| std::vector<std::unique_ptr<RTCRtpReceiverPlatform>> receivers_added; |
| std::vector<std::unique_ptr<RTCRtpReceiverPlatform>> receivers_removed; |
| EXPECT_CALL(*mock_client_.get(), DidModifyReceiversPlanBForMock(_, _, _)) |
| .WillRepeatedly(Invoke( |
| [&receivers_added, &receivers_removed]( |
| webrtc::PeerConnectionInterface::SignalingState signaling_state, |
| Vector<std::unique_ptr<RTCRtpReceiverPlatform>>* |
| platform_receivers_added, |
| Vector<std::unique_ptr<RTCRtpReceiverPlatform>>* |
| platform_receivers_removed) { |
| if (!platform_receivers_added->IsEmpty()) { |
| receivers_added.push_back( |
| std::move((*platform_receivers_added)[0])); |
| } |
| if (!platform_receivers_removed->IsEmpty()) { |
| receivers_removed.push_back( |
| std::move((*platform_receivers_removed)[0])); |
| } |
| })); |
| EXPECT_CALL( |
| *mock_tracker_.get(), |
| TrackAddTransceiver( |
| pc_handler_.get(), |
| PeerConnectionTracker::TransceiverUpdatedReason::kAddTrack, _, _)) |
| .Times(2); |
| EXPECT_CALL( |
| *mock_tracker_.get(), |
| TrackRemoveTransceiver( |
| pc_handler_.get(), |
| PeerConnectionTracker::TransceiverUpdatedReason::kRemoveTrack, _, _)) |
| .Times(2); |
| |
| InvokeOnAddStream(remote_stream); |
| RunMessageLoopsUntilIdle(); |
| EXPECT_TRUE(HasReceiverForEveryTrack(remote_stream, receivers_added)); |
| InvokeOnRemoveStream(remote_stream); |
| RunMessageLoopsUntilIdle(); |
| |
| EXPECT_EQ(receivers_added.size(), 2u); |
| EXPECT_EQ(receivers_added.size(), receivers_removed.size()); |
| EXPECT_EQ(receivers_added[0]->Id(), receivers_removed[0]->Id()); |
| EXPECT_EQ(receivers_added[1]->Id(), receivers_removed[1]->Id()); |
| } |
| |
| TEST_F(RTCPeerConnectionHandlerTest, OnIceCandidate) { |
| testing::InSequence sequence; |
| EXPECT_CALL(*mock_tracker_.get(), |
| TrackAddIceCandidate(pc_handler_.get(), _, |
| PeerConnectionTracker::SOURCE_LOCAL, true)); |
| EXPECT_CALL(*mock_client_.get(), DidGenerateICECandidate(_)); |
| |
| std::unique_ptr<webrtc::IceCandidateInterface> native_candidate( |
| mock_dependency_factory_->CreateIceCandidate("sdpMid", 1, kDummySdp)); |
| pc_handler_->observer()->OnIceCandidate(native_candidate.get()); |
| RunMessageLoopsUntilIdle(); |
| EXPECT_EQ("sdpMid", mock_client_->candidate_mid()); |
| EXPECT_EQ(1, mock_client_->candidate_mlineindex()); |
| EXPECT_EQ(kDummySdp, mock_client_->candidate_sdp()); |
| } |
| |
| TEST_F(RTCPeerConnectionHandlerTest, OnRenegotiationNeeded) { |
| testing::InSequence sequence; |
| EXPECT_CALL(*mock_tracker_.get(), |
| TrackOnRenegotiationNeeded(pc_handler_.get())); |
| EXPECT_CALL(*mock_client_.get(), NegotiationNeeded()); |
| pc_handler_->observer()->OnNegotiationNeededEvent(42); |
| } |
| |
| TEST_F(RTCPeerConnectionHandlerTest, CreateDataChannel) { |
| blink::WebString label = "d1"; |
| EXPECT_CALL(*mock_tracker_.get(), |
| TrackCreateDataChannel(pc_handler_.get(), testing::NotNull(), |
| PeerConnectionTracker::SOURCE_LOCAL)); |
| scoped_refptr<webrtc::DataChannelInterface> channel = |
| pc_handler_->CreateDataChannel("d1", webrtc::DataChannelInit()); |
| EXPECT_TRUE(channel.get()); |
| EXPECT_EQ(label.Utf8(), channel->label()); |
| } |
| |
| TEST_F(RTCPeerConnectionHandlerTest, CheckInsertableStreamsConfig) { |
| for (bool force_encoded_audio_insertable_streams : {true, false}) { |
| for (bool force_encoded_video_insertable_streams : {true, false}) { |
| auto handler = std::make_unique<RTCPeerConnectionHandlerUnderTest>( |
| mock_client_.get(), mock_dependency_factory_.get(), |
| force_encoded_audio_insertable_streams, |
| force_encoded_video_insertable_streams); |
| EXPECT_EQ(handler->force_encoded_audio_insertable_streams(), |
| force_encoded_audio_insertable_streams); |
| EXPECT_EQ(handler->force_encoded_video_insertable_streams(), |
| force_encoded_video_insertable_streams); |
| } |
| } |
| } |
| |
| TEST_F(RTCPeerConnectionHandlerTest, ThermalResourceDefaultValue) { |
| EXPECT_TRUE(mock_peer_connection_->adaptation_resources().IsEmpty()); |
| pc_handler_->OnThermalStateChange( |
| mojom::blink::DeviceThermalState::kCritical); |
| #if defined(OS_MAC) |
| bool expect_disabled = false; |
| #else |
| bool expect_disabled = true; |
| #endif |
| // A ThermalResource is created in response to the thermal signal. |
| EXPECT_EQ(mock_peer_connection_->adaptation_resources().IsEmpty(), |
| expect_disabled); |
| } |
| |
| TEST_F(RTCPeerConnectionHandlerTest, |
| ThermalStateChangeDoesNothingIfThermalResourceIsDisabled) { |
| // Overwrite base::Feature kWebRtcThermalResource's default to DISABLED. |
| base::test::ScopedFeatureList scoped_feature_list; |
| scoped_feature_list.InitAndDisableFeature(kWebRtcThermalResource); |
| |
| EXPECT_TRUE(mock_peer_connection_->adaptation_resources().IsEmpty()); |
| pc_handler_->OnThermalStateChange( |
| mojom::blink::DeviceThermalState::kCritical); |
| // A ThermalResource is created in response to the thermal signal. |
| EXPECT_TRUE(mock_peer_connection_->adaptation_resources().IsEmpty()); |
| } |
| |
| TEST_F(RTCPeerConnectionHandlerTest, |
| ThermalStateChangeTriggersThermalResourceIfEnabled) { |
| // Overwrite base::Feature kWebRtcThermalResource's default to ENABLED. |
| base::test::ScopedFeatureList scoped_feature_list; |
| scoped_feature_list.InitAndEnableFeature(kWebRtcThermalResource); |
| |
| EXPECT_TRUE(mock_peer_connection_->adaptation_resources().IsEmpty()); |
| // ThermalResource is created and injected on the fly. |
| pc_handler_->OnThermalStateChange( |
| mojom::blink::DeviceThermalState::kCritical); |
| auto resources = mock_peer_connection_->adaptation_resources(); |
| ASSERT_EQ(1u, resources.size()); |
| auto thermal_resource = resources[0]; |
| EXPECT_EQ("ThermalResource", thermal_resource->Name()); |
| // The initial kOveruse is observed. |
| FakeResourceListener resource_listener; |
| thermal_resource->SetResourceListener(&resource_listener); |
| EXPECT_EQ(1u, resource_listener.measurement_count()); |
| EXPECT_EQ(webrtc::ResourceUsageState::kOveruse, |
| resource_listener.latest_measurement()); |
| // ThermalResource responds to new measurements. |
| pc_handler_->OnThermalStateChange(mojom::blink::DeviceThermalState::kNominal); |
| EXPECT_EQ(2u, resource_listener.measurement_count()); |
| EXPECT_EQ(webrtc::ResourceUsageState::kUnderuse, |
| resource_listener.latest_measurement()); |
| } |
| |
| TEST_F(RTCPeerConnectionHandlerTest, |
| ThermalStateUmaListenerCreatedWhenVideoStreamAdded) { |
| base::HistogramTester histogram; |
| EXPECT_FALSE(pc_handler_->HasThermalUmaListner()); |
| MediaStreamDescriptor* local_stream = CreateLocalMediaStream("local_stream"); |
| EXPECT_TRUE(AddStream(local_stream)); |
| EXPECT_TRUE(pc_handler_->HasThermalUmaListner()); |
| } |
| |
| } // namespace blink |