| // 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 "services/audio/output_stream.h" |
| |
| #include <utility> |
| |
| #include "base/functional/bind.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/test/mock_callback.h" |
| #include "base/test/task_environment.h" |
| #include "base/unguessable_token.h" |
| #include "media/audio/audio_io.h" |
| #include "media/audio/mock_audio_manager.h" |
| #include "media/audio/test_audio_thread.h" |
| #include "mojo/public/cpp/bindings/associated_receiver.h" |
| #include "mojo/public/cpp/bindings/receiver.h" |
| #include "mojo/public/cpp/system/functions.h" |
| #include "services/audio/stream_factory.h" |
| #include "services/audio/test/mock_log.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using testing::AtMost; |
| using testing::DeleteArg; |
| using testing::Mock; |
| using testing::NiceMock; |
| using testing::NotNull; |
| using testing::Return; |
| using testing::SaveArg; |
| using testing::StrictMock; |
| using testing::_; |
| |
| namespace audio { |
| |
| namespace { |
| |
| // Aliases for use with MockCreatedCallback::Created(). |
| const bool successfully_ = true; |
| const bool unsuccessfully_ = false; |
| |
| class MockStream : public media::AudioOutputStream { |
| public: |
| MockStream() {} |
| |
| MockStream(const MockStream&) = delete; |
| MockStream& operator=(const MockStream&) = delete; |
| |
| MOCK_METHOD0(Open, bool()); |
| MOCK_METHOD1(Start, void(AudioSourceCallback* callback)); |
| MOCK_METHOD0(Stop, void()); |
| MOCK_METHOD1(SetVolume, void(double volume)); |
| MOCK_METHOD1(GetVolume, void(double* volume)); |
| MOCK_METHOD0(Close, void()); |
| MOCK_METHOD0(Flush, void()); |
| }; |
| |
| const uint32_t kPlatformErrorDisconnectReason = static_cast<uint32_t>( |
| media::mojom::AudioOutputStreamObserver::DisconnectReason::kPlatformError); |
| const uint32_t kTerminatedByClientDisconnectReason = |
| static_cast<uint32_t>(media::mojom::AudioOutputStreamObserver:: |
| DisconnectReason::kTerminatedByClient); |
| |
| class MockObserver : public media::mojom::AudioOutputStreamObserver { |
| public: |
| MockObserver() = default; |
| |
| MockObserver(const MockObserver&) = delete; |
| MockObserver& operator=(const MockObserver&) = delete; |
| |
| mojo::PendingAssociatedRemote<media::mojom::AudioOutputStreamObserver> |
| MakeRemote() { |
| DCHECK(!receiver_.is_bound()); |
| mojo::PendingAssociatedRemote<media::mojom::AudioOutputStreamObserver> |
| remote; |
| receiver_.Bind(remote.InitWithNewEndpointAndPassReceiver()); |
| receiver_.set_disconnect_with_reason_handler(base::BindOnce( |
| &MockObserver::BindingConnectionError, base::Unretained(this))); |
| return remote; |
| } |
| |
| void CloseBinding() { receiver_.reset(); } |
| |
| MOCK_METHOD0(DidStartPlaying, void()); |
| MOCK_METHOD0(DidStopPlaying, void()); |
| MOCK_METHOD1(DidChangeAudibleState, void(bool)); |
| |
| MOCK_METHOD2(BindingConnectionError, |
| void(uint32_t /*disconnect_reason*/, const std::string&)); |
| |
| private: |
| mojo::AssociatedReceiver<media::mojom::AudioOutputStreamObserver> receiver_{ |
| this}; |
| }; |
| |
| class MockCreatedCallback { |
| public: |
| MockCreatedCallback() {} |
| |
| MockCreatedCallback(const MockCreatedCallback&) = delete; |
| MockCreatedCallback& operator=(const MockCreatedCallback&) = delete; |
| |
| MOCK_METHOD1(Created, void(bool /*valid*/)); |
| |
| void OnCreated(media::mojom::ReadWriteAudioDataPipePtr ptr) { |
| Created(!!ptr); |
| } |
| |
| OutputStream::CreatedCallback Get() { |
| return base::BindOnce(&MockCreatedCallback::OnCreated, |
| base::Unretained(this)); |
| } |
| }; |
| |
| } // namespace |
| |
| // Instantiates various classes that we're going to want in most test cases. |
| class TestEnvironment { |
| public: |
| TestEnvironment() |
| : audio_manager_(std::make_unique<media::TestAudioThread>(false)), |
| stream_factory_(&audio_manager_, /*aecdump_recording_manager=*/nullptr), |
| stream_factory_receiver_( |
| &stream_factory_, |
| remote_stream_factory_.BindNewPipeAndPassReceiver()) { |
| mojo::SetDefaultProcessErrorHandler(bad_message_callback_.Get()); |
| } |
| |
| TestEnvironment(const TestEnvironment&) = delete; |
| TestEnvironment& operator=(const TestEnvironment&) = delete; |
| |
| ~TestEnvironment() { |
| audio_manager_.Shutdown(); |
| mojo::SetDefaultProcessErrorHandler(base::NullCallback()); |
| } |
| |
| using MockDeleteCallback = base::MockCallback<OutputStream::DeleteCallback>; |
| using MockBadMessageCallback = |
| base::MockCallback<base::RepeatingCallback<void(const std::string&)>>; |
| |
| mojo::PendingRemote<media::mojom::AudioOutputStream> CreateStream() { |
| mojo::PendingRemote<media::mojom::AudioOutputStream> remote_stream; |
| remote_stream_factory_->CreateOutputStream( |
| remote_stream.InitWithNewPipeAndPassReceiver(), observer_.MakeRemote(), |
| log_.MakeRemote(), "", |
| media::AudioParameters::UnavailableDeviceParams(), |
| base::UnguessableToken::Create(), created_callback_.Get()); |
| return remote_stream; |
| } |
| |
| mojo::PendingRemote<media::mojom::AudioOutputStream> |
| CreateStreamWithNullptrObserver() { |
| mojo::PendingRemote<media::mojom::AudioOutputStream> remote_stream; |
| remote_stream_factory_->CreateOutputStream( |
| remote_stream.InitWithNewPipeAndPassReceiver(), |
| mojo::NullAssociatedRemote(), log_.MakeRemote(), "", |
| media::AudioParameters::UnavailableDeviceParams(), |
| base::UnguessableToken::Create(), created_callback_.Get()); |
| return remote_stream; |
| } |
| |
| mojo::PendingRemote<media::mojom::AudioOutputStream> |
| CreateStreamWithNullptrLog() { |
| mojo::PendingRemote<media::mojom::AudioOutputStream> remote_stream; |
| remote_stream_factory_->CreateOutputStream( |
| remote_stream.InitWithNewPipeAndPassReceiver(), observer_.MakeRemote(), |
| mojo::NullRemote(), "", |
| media::AudioParameters::UnavailableDeviceParams(), |
| base::UnguessableToken::Create(), created_callback_.Get()); |
| return remote_stream; |
| } |
| |
| media::MockAudioManager& audio_manager() { return audio_manager_; } |
| |
| MockObserver& observer() { return observer_; } |
| |
| MockLog& log() { return log_; } |
| |
| MockCreatedCallback& created_callback() { return created_callback_; } |
| |
| MockBadMessageCallback& bad_message_callback() { |
| return bad_message_callback_; |
| } |
| |
| media::mojom::AudioStreamFactory* remote_stream_factory() { |
| return remote_stream_factory_.get(); |
| } |
| |
| private: |
| base::test::TaskEnvironment tasks_; |
| media::MockAudioManager audio_manager_; |
| StreamFactory stream_factory_; |
| mojo::Remote<media::mojom::AudioStreamFactory> remote_stream_factory_; |
| mojo::Receiver<media::mojom::AudioStreamFactory> stream_factory_receiver_; |
| StrictMock<MockObserver> observer_; |
| NiceMock<MockLog> log_; |
| StrictMock<MockCreatedCallback> created_callback_; |
| StrictMock<MockBadMessageCallback> bad_message_callback_; |
| }; |
| |
| TEST(AudioServiceOutputStreamTest, ConstructDestruct) { |
| TestEnvironment env; |
| MockStream mock_stream; |
| EXPECT_CALL(env.created_callback(), Created(successfully_)); |
| env.audio_manager().SetMakeOutputStreamCB(base::BindRepeating( |
| [](media::AudioOutputStream* stream, const media::AudioParameters& params, |
| const std::string& device_id) { return stream; }, |
| &mock_stream)); |
| |
| EXPECT_CALL(mock_stream, Open()).WillOnce(Return(true)); |
| EXPECT_CALL(mock_stream, SetVolume(1)); |
| EXPECT_CALL(env.log(), OnCreated(_, _)); |
| |
| mojo::Remote<media::mojom::AudioOutputStream> stream(env.CreateStream()); |
| base::RunLoop().RunUntilIdle(); |
| Mock::VerifyAndClear(&mock_stream); |
| Mock::VerifyAndClear(&env.created_callback()); |
| |
| EXPECT_CALL(env.log(), OnClosed()); |
| EXPECT_CALL(mock_stream, Close()); |
| EXPECT_CALL(env.observer(), |
| BindingConnectionError(kTerminatedByClientDisconnectReason, _)); |
| stream.reset(); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| TEST(AudioServiceOutputStreamTest, ConstructDestructNullptrObserver) { |
| TestEnvironment env; |
| MockStream mock_stream; |
| EXPECT_CALL(env.created_callback(), Created(successfully_)); |
| env.audio_manager().SetMakeOutputStreamCB(base::BindRepeating( |
| [](media::AudioOutputStream* stream, const media::AudioParameters& params, |
| const std::string& device_id) { return stream; }, |
| &mock_stream)); |
| |
| EXPECT_CALL(mock_stream, Open()).WillOnce(Return(true)); |
| EXPECT_CALL(mock_stream, SetVolume(1)); |
| EXPECT_CALL(env.log(), OnCreated(_, _)); |
| |
| mojo::Remote<media::mojom::AudioOutputStream> stream( |
| env.CreateStreamWithNullptrObserver()); |
| base::RunLoop().RunUntilIdle(); |
| Mock::VerifyAndClear(&mock_stream); |
| Mock::VerifyAndClear(&env.created_callback()); |
| |
| EXPECT_CALL(env.log(), OnClosed()); |
| EXPECT_CALL(mock_stream, Close()); |
| stream.reset(); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| TEST(AudioServiceOutputStreamTest, ConstructDestructNullptrLog) { |
| TestEnvironment env; |
| MockStream mock_stream; |
| EXPECT_CALL(env.created_callback(), Created(successfully_)); |
| env.audio_manager().SetMakeOutputStreamCB(base::BindRepeating( |
| [](media::AudioOutputStream* stream, const media::AudioParameters& params, |
| const std::string& device_id) { return stream; }, |
| &mock_stream)); |
| |
| EXPECT_CALL(mock_stream, Open()).WillOnce(Return(true)); |
| EXPECT_CALL(mock_stream, SetVolume(1)); |
| |
| mojo::Remote<media::mojom::AudioOutputStream> stream( |
| env.CreateStreamWithNullptrLog()); |
| base::RunLoop().RunUntilIdle(); |
| Mock::VerifyAndClear(&mock_stream); |
| Mock::VerifyAndClear(&env.created_callback()); |
| |
| EXPECT_CALL(mock_stream, Close()); |
| EXPECT_CALL(env.observer(), |
| BindingConnectionError(kTerminatedByClientDisconnectReason, _)); |
| stream.reset(); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| TEST(AudioServiceOutputStreamTest, |
| ConstructStreamAndDestructObserver_DestructsStream) { |
| TestEnvironment env; |
| MockStream mock_stream; |
| env.audio_manager().SetMakeOutputStreamCB(base::BindRepeating( |
| [](media::AudioOutputStream* stream, const media::AudioParameters& params, |
| const std::string& device_id) { return stream; }, |
| &mock_stream)); |
| |
| EXPECT_CALL(env.created_callback(), Created(successfully_)); |
| EXPECT_CALL(mock_stream, Open()).WillOnce(Return(true)); |
| EXPECT_CALL(mock_stream, SetVolume(1)); |
| |
| mojo::Remote<media::mojom::AudioOutputStream> stream(env.CreateStream()); |
| base::RunLoop().RunUntilIdle(); |
| Mock::VerifyAndClear(&mock_stream); |
| Mock::VerifyAndClear(&env.created_callback()); |
| |
| EXPECT_CALL(mock_stream, Close()); |
| |
| env.observer().CloseBinding(); |
| base::RunLoop().RunUntilIdle(); |
| |
| Mock::VerifyAndClear(&mock_stream); |
| } |
| |
| TEST(AudioServiceOutputStreamTest, |
| ConstructStreamAndReleaseStreamPtr_DestructsStream) { |
| TestEnvironment env; |
| MockStream mock_stream; |
| env.audio_manager().SetMakeOutputStreamCB(base::BindRepeating( |
| [](media::AudioOutputStream* stream, const media::AudioParameters& params, |
| const std::string& device_id) { return stream; }, |
| &mock_stream)); |
| |
| EXPECT_CALL(env.created_callback(), Created(successfully_)); |
| EXPECT_CALL(mock_stream, Open()).WillOnce(Return(true)); |
| EXPECT_CALL(mock_stream, SetVolume(1)); |
| |
| mojo::Remote<media::mojom::AudioOutputStream> stream(env.CreateStream()); |
| base::RunLoop().RunUntilIdle(); |
| Mock::VerifyAndClear(&mock_stream); |
| Mock::VerifyAndClear(&env.created_callback()); |
| |
| EXPECT_CALL(mock_stream, Close()); |
| EXPECT_CALL(env.observer(), |
| BindingConnectionError(kTerminatedByClientDisconnectReason, _)); |
| |
| stream.reset(); |
| base::RunLoop().RunUntilIdle(); |
| |
| Mock::VerifyAndClear(&mock_stream); |
| Mock::VerifyAndClear(&env.observer()); |
| } |
| |
| TEST(AudioServiceOutputStreamTest, Play_Plays) { |
| TestEnvironment env; |
| MockStream mock_stream; |
| EXPECT_CALL(env.created_callback(), Created(successfully_)); |
| env.audio_manager().SetMakeOutputStreamCB(base::BindRepeating( |
| [](media::AudioOutputStream* stream, const media::AudioParameters& params, |
| const std::string& device_id) { return stream; }, |
| &mock_stream)); |
| |
| EXPECT_CALL(mock_stream, Open()).WillOnce(Return(true)); |
| EXPECT_CALL(mock_stream, SetVolume(1)); |
| |
| mojo::Remote<media::mojom::AudioOutputStream> stream(env.CreateStream()); |
| base::RunLoop().RunUntilIdle(); |
| Mock::VerifyAndClear(&mock_stream); |
| Mock::VerifyAndClear(&env.created_callback()); |
| |
| EXPECT_CALL(mock_stream, Start(NotNull())); |
| EXPECT_CALL(env.log(), OnStarted()); |
| EXPECT_CALL(env.observer(), DidStartPlaying()); |
| // May or may not get an audibility notification depending on if power |
| // monitoring is enabled. |
| EXPECT_CALL(env.observer(), DidChangeAudibleState(true)).Times(AtMost(1)); |
| stream->Play(); |
| base::RunLoop().RunUntilIdle(); |
| Mock::VerifyAndClear(&mock_stream); |
| Mock::VerifyAndClear(&env.observer()); |
| |
| EXPECT_CALL(mock_stream, Stop()); |
| EXPECT_CALL(mock_stream, Close()); |
| EXPECT_CALL(env.observer(), DidChangeAudibleState(false)).Times(AtMost(1)); |
| EXPECT_CALL(env.observer(), DidStopPlaying()).Times(AtMost(1)); |
| EXPECT_CALL(env.observer(), |
| BindingConnectionError(kTerminatedByClientDisconnectReason, _)); |
| stream.reset(); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| TEST(AudioServiceOutputStreamTest, PlayAndPause_PlaysAndStops) { |
| TestEnvironment env; |
| MockStream mock_stream; |
| EXPECT_CALL(env.created_callback(), Created(successfully_)); |
| env.audio_manager().SetMakeOutputStreamCB(base::BindRepeating( |
| [](media::AudioOutputStream* stream, const media::AudioParameters& params, |
| const std::string& device_id) { return stream; }, |
| &mock_stream)); |
| |
| EXPECT_CALL(mock_stream, Open()).WillOnce(Return(true)); |
| EXPECT_CALL(mock_stream, SetVolume(1)); |
| |
| mojo::Remote<media::mojom::AudioOutputStream> stream(env.CreateStream()); |
| base::RunLoop().RunUntilIdle(); |
| Mock::VerifyAndClear(&mock_stream); |
| Mock::VerifyAndClear(&env.created_callback()); |
| |
| EXPECT_CALL(mock_stream, Start(NotNull())); |
| EXPECT_CALL(env.observer(), DidStartPlaying()); |
| // May or may not get an audibility notification depending on if power |
| // monitoring is enabled. |
| EXPECT_CALL(env.observer(), DidChangeAudibleState(true)).Times(AtMost(1)); |
| stream->Play(); |
| base::RunLoop().RunUntilIdle(); |
| Mock::VerifyAndClear(&mock_stream); |
| Mock::VerifyAndClear(&env.observer()); |
| |
| EXPECT_CALL(mock_stream, Stop()); |
| EXPECT_CALL(env.log(), OnStopped()); |
| EXPECT_CALL(env.observer(), DidChangeAudibleState(false)).Times(AtMost(1)); |
| EXPECT_CALL(env.observer(), DidStopPlaying()); |
| stream->Pause(); |
| base::RunLoop().RunUntilIdle(); |
| Mock::VerifyAndClear(&mock_stream); |
| Mock::VerifyAndClear(&env.observer()); |
| |
| EXPECT_CALL(mock_stream, Close()); |
| EXPECT_CALL(env.observer(), |
| BindingConnectionError(kTerminatedByClientDisconnectReason, _)); |
| stream.reset(); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| TEST(AudioServiceOutputStreamTest, SetVolume_SetsVolume) { |
| double new_volume = 0.618; |
| TestEnvironment env; |
| MockStream mock_stream; |
| EXPECT_CALL(env.created_callback(), Created(successfully_)); |
| env.audio_manager().SetMakeOutputStreamCB(base::BindRepeating( |
| [](media::AudioOutputStream* stream, const media::AudioParameters& params, |
| const std::string& device_id) { return stream; }, |
| &mock_stream)); |
| |
| EXPECT_CALL(mock_stream, Open()).WillOnce(Return(true)); |
| EXPECT_CALL(mock_stream, SetVolume(1)); |
| |
| mojo::Remote<media::mojom::AudioOutputStream> stream(env.CreateStream()); |
| base::RunLoop().RunUntilIdle(); |
| Mock::VerifyAndClear(&mock_stream); |
| Mock::VerifyAndClear(&env.created_callback()); |
| |
| EXPECT_CALL(mock_stream, SetVolume(new_volume)); |
| EXPECT_CALL(env.log(), OnSetVolume(new_volume)); |
| stream->SetVolume(new_volume); |
| base::RunLoop().RunUntilIdle(); |
| Mock::VerifyAndClear(&mock_stream); |
| |
| EXPECT_CALL(mock_stream, Close()); |
| EXPECT_CALL(env.observer(), |
| BindingConnectionError(kTerminatedByClientDisconnectReason, _)); |
| stream.reset(); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| TEST(AudioServiceOutputStreamTest, SetNegativeVolume_BadMessage) { |
| TestEnvironment env; |
| MockStream mock_stream; |
| EXPECT_CALL(env.created_callback(), Created(successfully_)); |
| env.audio_manager().SetMakeOutputStreamCB(base::BindRepeating( |
| [](media::AudioOutputStream* stream, const media::AudioParameters& params, |
| const std::string& device_id) { return stream; }, |
| &mock_stream)); |
| |
| EXPECT_CALL(mock_stream, Open()).WillOnce(Return(true)); |
| EXPECT_CALL(mock_stream, SetVolume(1)); |
| |
| mojo::Remote<media::mojom::AudioOutputStream> stream(env.CreateStream()); |
| base::RunLoop().RunUntilIdle(); |
| Mock::VerifyAndClear(&mock_stream); |
| Mock::VerifyAndClear(&env.created_callback()); |
| |
| EXPECT_CALL(mock_stream, Close()); |
| EXPECT_CALL(env.observer(), |
| BindingConnectionError(kPlatformErrorDisconnectReason, _)); |
| EXPECT_CALL(env.bad_message_callback(), Run(_)); |
| stream->SetVolume(-0.1); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| TEST(AudioServiceOutputStreamTest, SetVolumeGreaterThanOne_BadMessage) { |
| TestEnvironment env; |
| MockStream mock_stream; |
| EXPECT_CALL(env.created_callback(), Created(successfully_)); |
| env.audio_manager().SetMakeOutputStreamCB(base::BindRepeating( |
| [](media::AudioOutputStream* stream, const media::AudioParameters& params, |
| const std::string& device_id) { return stream; }, |
| &mock_stream)); |
| |
| EXPECT_CALL(mock_stream, Open()).WillOnce(Return(true)); |
| EXPECT_CALL(mock_stream, SetVolume(1)); |
| |
| mojo::Remote<media::mojom::AudioOutputStream> stream(env.CreateStream()); |
| base::RunLoop().RunUntilIdle(); |
| Mock::VerifyAndClear(&mock_stream); |
| Mock::VerifyAndClear(&env.created_callback()); |
| |
| EXPECT_CALL(mock_stream, Close()); |
| EXPECT_CALL(env.observer(), |
| BindingConnectionError(kPlatformErrorDisconnectReason, _)); |
| EXPECT_CALL(env.bad_message_callback(), Run(_)); |
| stream->SetVolume(1.1); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| TEST(AudioServiceOutputStreamTest, |
| ConstructWithStreamCreationFailure_SignalsError) { |
| TestEnvironment env; |
| |
| // By default, the MockAudioManager fails to create a stream. |
| |
| mojo::Remote<media::mojom::AudioOutputStream> stream(env.CreateStream()); |
| |
| EXPECT_CALL(env.created_callback(), Created(unsuccessfully_)); |
| EXPECT_CALL(env.observer(), |
| BindingConnectionError(kPlatformErrorDisconnectReason, _)); |
| EXPECT_CALL(env.log(), OnError()); |
| base::RunLoop().RunUntilIdle(); |
| Mock::VerifyAndClear(&env.observer()); |
| } |
| |
| TEST(AudioServiceOutputStreamTest, |
| ConstructWithStreamCreationFailureAndDestructBeforeErrorFires_NoCrash) { |
| // The main purpose of this test is to make sure that that delete callback |
| // call is deferred, and that it is canceled in case of destruction. |
| TestEnvironment env; |
| |
| // By default, the MockAudioManager fails to create a stream. |
| |
| mojo::Remote<media::mojom::AudioOutputStream> stream(env.CreateStream()); |
| EXPECT_CALL(env.created_callback(), Created(unsuccessfully_)); |
| |
| EXPECT_CALL(env.observer(), |
| BindingConnectionError(kPlatformErrorDisconnectReason, _)); |
| |
| base::RunLoop().RunUntilIdle(); |
| Mock::VerifyAndClear(&env.observer()); |
| } |
| |
| TEST(AudioServiceOutputStreamTest, BindMuters) { |
| // Set up the test environment. |
| TestEnvironment env; |
| MockStream mock_stream; |
| EXPECT_CALL(env.created_callback(), Created(successfully_)); |
| env.audio_manager().SetMakeOutputStreamCB(base::BindRepeating( |
| [](media::AudioOutputStream* stream, const media::AudioParameters& params, |
| const std::string& device_id) { return stream; }, |
| &mock_stream)); |
| |
| EXPECT_CALL(mock_stream, Open()).WillOnce(Return(true)); |
| EXPECT_CALL(mock_stream, SetVolume(1)); |
| EXPECT_CALL(env.log(), OnCreated(_, _)); |
| |
| mojo::Remote<media::mojom::AudioOutputStream> stream(env.CreateStream()); |
| base::RunLoop().RunUntilIdle(); |
| Mock::VerifyAndClear(&mock_stream); |
| Mock::VerifyAndClear(&env.created_callback()); |
| |
| // Bind the first muter. |
| mojo::AssociatedRemote<media::mojom::LocalMuter> muter1; |
| base::UnguessableToken group_id = base::UnguessableToken::Create(); |
| env.remote_stream_factory()->BindMuter( |
| muter1.BindNewEndpointAndPassReceiver(), group_id); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Unbind the first muter and immediately bind the second muter. The muter |
| // should not be destroyed in this case. |
| muter1.reset(); |
| mojo::AssociatedRemote<media::mojom::LocalMuter> muter2; |
| env.remote_stream_factory()->BindMuter( |
| muter2.BindNewEndpointAndPassReceiver(), group_id); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_CALL(env.log(), OnClosed()); |
| EXPECT_CALL(mock_stream, Close()); |
| EXPECT_CALL(env.observer(), |
| BindingConnectionError(kTerminatedByClientDisconnectReason, _)); |
| stream.reset(); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| } // namespace audio |