| // Copyright 2018 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/mirroring/service/captured_audio_input.h" |
| |
| #include <utility> |
| |
| #include "base/functional/bind.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/run_loop.h" |
| #include "base/test/task_environment.h" |
| #include "components/mirroring/mojom/session_observer.mojom.h" |
| #include "media/base/audio_capturer_source.h" |
| #include "media/base/audio_parameters.h" |
| #include "media/mojo/mojom/audio_data_pipe.mojom.h" |
| #include "mojo/public/cpp/bindings/pending_remote.h" |
| #include "mojo/public/cpp/bindings/remote.h" |
| #include "mojo/public/cpp/bindings/self_owned_receiver.h" |
| #include "mojo/public/cpp/system/buffer.h" |
| #include "mojo/public/cpp/system/platform_handle.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using ::testing::InvokeWithoutArgs; |
| |
| namespace mirroring { |
| |
| using AudioSourceErrorCode = media::AudioCapturerSource::ErrorCode; |
| |
| namespace { |
| |
| class MockStream final : public media::mojom::AudioInputStream { |
| public: |
| MOCK_METHOD0(Record, void()); |
| MOCK_METHOD1(SetVolume, void(double)); |
| }; |
| |
| class MockDelegate final : public media::AudioInputIPCDelegate { |
| public: |
| MockDelegate() = default; |
| ~MockDelegate() override = default; |
| |
| MOCK_METHOD0(StreamCreated, void()); |
| MOCK_METHOD1(OnError, void(AudioSourceErrorCode code)); |
| MOCK_METHOD1(OnMuted, void(bool muted)); |
| MOCK_METHOD0(OnIPCClosed, void()); |
| |
| void OnStreamCreated(base::UnsafeSharedMemoryRegion shared_memory_region, |
| base::SyncSocket::ScopedHandle socket_handle, |
| bool initially_muted) override { |
| StreamCreated(); |
| } |
| }; |
| |
| } // namespace |
| |
| class CapturedAudioInputTest : public ::testing::Test { |
| public: |
| CapturedAudioInputTest() = default; |
| |
| CapturedAudioInputTest(const CapturedAudioInputTest&) = delete; |
| CapturedAudioInputTest& operator=(const CapturedAudioInputTest&) = delete; |
| |
| ~CapturedAudioInputTest() override { task_environment_.RunUntilIdle(); } |
| |
| void CreateMockStream( |
| mojo::PendingRemote<mojom::AudioStreamCreatorClient> client, |
| const media::AudioParameters& params, |
| uint32_t total_segments) { |
| EXPECT_FALSE(socket_.IsValid()); |
| EXPECT_FALSE(stream_); |
| mojo::PendingRemote<media::mojom::AudioInputStream> pending_stream; |
| auto input_stream = std::make_unique<MockStream>(); |
| stream_ = input_stream.get(); |
| mojo::MakeSelfOwnedReceiver( |
| std::move(input_stream), |
| pending_stream.InitWithNewPipeAndPassReceiver()); |
| base::CancelableSyncSocket foreign_socket; |
| EXPECT_TRUE( |
| base::CancelableSyncSocket::CreatePair(&socket_, &foreign_socket)); |
| mojo::Remote<mojom::AudioStreamCreatorClient> audio_client( |
| std::move(client)); |
| stream_client_.reset(); |
| audio_client->StreamCreated( |
| std::move(pending_stream), stream_client_.BindNewPipeAndPassReceiver(), |
| {std::in_place, base::UnsafeSharedMemoryRegion::Create(1024), |
| mojo::PlatformHandle(foreign_socket.Take())}); |
| } |
| |
| protected: |
| void CreateStream() { |
| audio_input_ = std::make_unique<CapturedAudioInput>( |
| base::BindRepeating(&CapturedAudioInputTest::CreateMockStream, |
| base::Unretained(this)), |
| observer_); |
| base::RunLoop run_loop; |
| EXPECT_CALL(delegate_, StreamCreated()) |
| .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit)); |
| audio_input_->CreateStream(&delegate_, media::AudioParameters(), false, 10); |
| run_loop.Run(); |
| } |
| |
| void CloseStream() { |
| EXPECT_TRUE(audio_input_); |
| audio_input_->CloseStream(); |
| task_environment_.RunUntilIdle(); |
| socket_.Close(); |
| audio_input_.reset(); |
| stream_ = nullptr; |
| } |
| |
| void SignalStreamError() { |
| EXPECT_TRUE(stream_client_.is_bound()); |
| base::RunLoop run_loop; |
| EXPECT_CALL(delegate_, OnError(AudioSourceErrorCode::kUnknown)) |
| .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit)); |
| stream_client_->OnError(media::mojom::InputStreamErrorCode::kUnknown); |
| run_loop.Run(); |
| } |
| |
| void SignalStreamPermissionsError() { |
| EXPECT_TRUE(stream_client_.is_bound()); |
| base::RunLoop run_loop; |
| EXPECT_CALL(delegate_, OnError(AudioSourceErrorCode::kSystemPermissions)) |
| .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit)); |
| stream_client_->OnError( |
| media::mojom::InputStreamErrorCode::kSystemPermissions); |
| run_loop.Run(); |
| } |
| |
| void SignalMutedStateChanged(bool is_muted) { |
| EXPECT_TRUE(stream_client_.is_bound()); |
| base::RunLoop run_loop; |
| EXPECT_CALL(delegate_, OnMuted(true)) |
| .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit)); |
| stream_client_->OnMutedStateChanged(is_muted); |
| run_loop.Run(); |
| } |
| |
| void SetVolume(double volume) { |
| EXPECT_TRUE(audio_input_); |
| base::RunLoop run_loop; |
| EXPECT_CALL(*stream_, SetVolume(volume)) |
| .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit)); |
| audio_input_->SetVolume(volume); |
| run_loop.Run(); |
| } |
| |
| void Record() { |
| EXPECT_TRUE(audio_input_); |
| base::RunLoop run_loop; |
| EXPECT_CALL(*stream_, Record()) |
| .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit)); |
| audio_input_->RecordStream(); |
| run_loop.Run(); |
| } |
| |
| private: |
| base::test::TaskEnvironment task_environment_; |
| std::unique_ptr<media::AudioInputIPC> audio_input_; |
| MockDelegate delegate_; |
| raw_ptr<MockStream, AcrossTasksDanglingUntriaged> stream_ = nullptr; |
| mojo::Remote<media::mojom::AudioInputStreamClient> stream_client_; |
| mojo::Remote<mojom::SessionObserver> observer_; |
| base::CancelableSyncSocket socket_; |
| }; |
| |
| TEST_F(CapturedAudioInputTest, CreateStream) { |
| // Test that |delegate_| runs OnStreamCreated(). |
| CreateStream(); |
| CloseStream(); |
| } |
| |
| TEST_F(CapturedAudioInputTest, PropagatesStreamError) { |
| CreateStream(); |
| SignalStreamError(); |
| CloseStream(); |
| } |
| |
| TEST_F(CapturedAudioInputTest, PropagatesStreamPermissionsError) { |
| CreateStream(); |
| SignalStreamPermissionsError(); |
| CloseStream(); |
| } |
| |
| TEST_F(CapturedAudioInputTest, PropagatesMutedStateChange) { |
| CreateStream(); |
| SignalMutedStateChanged(true); |
| CloseStream(); |
| } |
| |
| TEST_F(CapturedAudioInputTest, SetVolume) { |
| CreateStream(); |
| SetVolume(0.8); |
| CloseStream(); |
| } |
| |
| TEST_F(CapturedAudioInputTest, Record) { |
| CreateStream(); |
| Record(); |
| CloseStream(); |
| } |
| |
| } // namespace mirroring |