blob: f0faa0bfa3902da6a74b5eb7eb1d90475397f533 [file] [log] [blame]
// Copyright 2017 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 "media/mojo/services/mojo_audio_output_stream.h"
#include <utility>
#include "base/bind.h"
#include "base/memory/ptr_util.h"
#include "base/memory/unsafe_shared_memory_region.h"
#include "base/run_loop.h"
#include "base/sync_socket.h"
#include "base/test/task_environment.h"
#include "media/mojo/mojom/audio_data_pipe.mojom.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/system/message_pipe.h"
#include "mojo/public/cpp/system/platform_handle.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
namespace {
const double kNewVolume = 0.618;
// Not actually used, but sent from the AudioOutputDelegate.
const int kStreamId = 0;
const int kShmemSize = 100;
using testing::_;
using testing::Mock;
using testing::NotNull;
using testing::Return;
using testing::SaveArg;
using testing::StrictMock;
using testing::Test;
class TestCancelableSyncSocket : public base::CancelableSyncSocket {
public:
TestCancelableSyncSocket() = default;
void ExpectOwnershipTransfer() { expect_ownership_transfer_ = true; }
TestCancelableSyncSocket(const TestCancelableSyncSocket&) = delete;
TestCancelableSyncSocket& operator=(const TestCancelableSyncSocket&) = delete;
~TestCancelableSyncSocket() override {
// When the handle is sent over mojo, mojo takes ownership over it and
// closes it. We have to make sure we do not also retain the handle in the
// sync socket, as the sync socket closes the handle on destruction.
if (expect_ownership_transfer_)
EXPECT_FALSE(IsValid());
}
private:
bool expect_ownership_transfer_ = false;
};
class MockDelegate : public AudioOutputDelegate {
public:
MockDelegate() = default;
~MockDelegate() override = default;
MOCK_METHOD0(GetStreamId, int());
MOCK_METHOD0(OnPlayStream, void());
MOCK_METHOD0(OnPauseStream, void());
MOCK_METHOD1(OnSetVolume, void(double));
MOCK_METHOD0(OnFlushStream, void());
};
class MockDelegateFactory {
public:
void PrepareDelegateForCreation(
std::unique_ptr<AudioOutputDelegate> delegate) {
ASSERT_EQ(nullptr, delegate_);
delegate_.swap(delegate);
}
std::unique_ptr<AudioOutputDelegate> CreateDelegate(
AudioOutputDelegate::EventHandler* handler) {
MockCreateDelegate(handler);
EXPECT_NE(nullptr, delegate_);
return std::move(delegate_);
}
MOCK_METHOD1(MockCreateDelegate, void(AudioOutputDelegate::EventHandler*));
private:
std::unique_ptr<AudioOutputDelegate> delegate_;
};
class MockDeleter {
public:
MOCK_METHOD1(Finished, void(bool));
};
class MockClient {
public:
MockClient() = default;
void Initialize(mojom::ReadWriteAudioDataPipePtr data_pipe) {
ASSERT_TRUE(data_pipe->shared_memory.IsValid());
ASSERT_TRUE(data_pipe->socket.is_valid());
socket_ = std::make_unique<base::CancelableSyncSocket>(
data_pipe->socket.TakePlatformFile());
EXPECT_TRUE(socket_->IsValid());
shared_memory_region_ = std::move(data_pipe->shared_memory);
GotNotification();
}
MOCK_METHOD0(GotNotification, void());
private:
base::UnsafeSharedMemoryRegion shared_memory_region_;
std::unique_ptr<base::CancelableSyncSocket> socket_;
};
std::unique_ptr<AudioOutputDelegate> CreateNoDelegate(
AudioOutputDelegate::EventHandler* event_handler) {
return nullptr;
}
void NotCalled(mojo::PendingRemote<mojom::AudioOutputStream>,
mojom::ReadWriteAudioDataPipePtr) {
ADD_FAILURE() << "The StreamCreated callback was called despite the test "
"expecting it not to.";
}
} // namespace
class MojoAudioOutputStreamTest : public Test {
public:
MojoAudioOutputStreamTest()
: foreign_socket_(std::make_unique<TestCancelableSyncSocket>()) {}
mojo::Remote<mojom::AudioOutputStream> CreateAudioOutput() {
mojo::Remote<mojom::AudioOutputStream> remote;
pending_stream_receiver = remote.BindNewPipeAndPassReceiver();
ExpectDelegateCreation();
impl_ = std::make_unique<MojoAudioOutputStream>(
base::BindOnce(&MockDelegateFactory::CreateDelegate,
base::Unretained(&mock_delegate_factory_)),
base::BindOnce(&MojoAudioOutputStreamTest::CreatedStream,
base::Unretained(this)),
base::BindOnce(&MockDeleter::Finished, base::Unretained(&deleter_)));
return remote;
}
protected:
void CreatedStream(mojo::PendingRemote<mojom::AudioOutputStream> stream,
mojom::ReadWriteAudioDataPipePtr data_pipe) {
EXPECT_EQ(mojo::FuseMessagePipes(pending_stream_receiver.PassPipe(),
stream.PassPipe()),
MOJO_RESULT_OK);
client_.Initialize(std::move(data_pipe));
}
void ExpectDelegateCreation() {
delegate_ = new StrictMock<MockDelegate>();
mock_delegate_factory_.PrepareDelegateForCreation(
base::WrapUnique(delegate_));
EXPECT_TRUE(
base::CancelableSyncSocket::CreatePair(&local_, foreign_socket_.get()));
mem_ = base::UnsafeSharedMemoryRegion::Create(kShmemSize);
EXPECT_TRUE(mem_.IsValid());
EXPECT_CALL(mock_delegate_factory_, MockCreateDelegate(NotNull()))
.WillOnce(SaveArg<0>(&delegate_event_handler_));
}
base::test::SingleThreadTaskEnvironment task_environment_;
base::CancelableSyncSocket local_;
std::unique_ptr<TestCancelableSyncSocket> foreign_socket_;
base::UnsafeSharedMemoryRegion mem_;
StrictMock<MockDelegate>* delegate_ = nullptr;
AudioOutputDelegate::EventHandler* delegate_event_handler_ = nullptr;
StrictMock<MockDelegateFactory> mock_delegate_factory_;
StrictMock<MockDeleter> deleter_;
StrictMock<MockClient> client_;
mojo::PendingReceiver<mojom::AudioOutputStream> pending_stream_receiver;
std::unique_ptr<MojoAudioOutputStream> impl_;
};
TEST_F(MojoAudioOutputStreamTest, NoDelegate_SignalsError) {
MojoAudioOutputStream stream(
base::BindOnce(&CreateNoDelegate), base::BindOnce(&NotCalled),
base::BindOnce(&MockDeleter::Finished, base::Unretained(&deleter_)));
EXPECT_CALL(deleter_, Finished(true));
base::RunLoop().RunUntilIdle();
}
TEST_F(MojoAudioOutputStreamTest, Play_Plays) {
mojo::Remote<mojom::AudioOutputStream> audio_output_remote =
CreateAudioOutput();
EXPECT_CALL(client_, GotNotification());
EXPECT_CALL(*delegate_, OnPlayStream());
delegate_event_handler_->OnStreamCreated(kStreamId, std::move(mem_),
std::move(foreign_socket_));
audio_output_remote->Play();
base::RunLoop().RunUntilIdle();
}
TEST_F(MojoAudioOutputStreamTest, Pause_Pauses) {
mojo::Remote<mojom::AudioOutputStream> audio_output_remote =
CreateAudioOutput();
EXPECT_CALL(client_, GotNotification());
EXPECT_CALL(*delegate_, OnPauseStream());
delegate_event_handler_->OnStreamCreated(kStreamId, std::move(mem_),
std::move(foreign_socket_));
audio_output_remote->Pause();
base::RunLoop().RunUntilIdle();
}
TEST_F(MojoAudioOutputStreamTest, SetVolume_SetsVolume) {
mojo::Remote<mojom::AudioOutputStream> audio_output_remote =
CreateAudioOutput();
EXPECT_CALL(client_, GotNotification());
EXPECT_CALL(*delegate_, OnSetVolume(kNewVolume));
delegate_event_handler_->OnStreamCreated(kStreamId, std::move(mem_),
std::move(foreign_socket_));
audio_output_remote->SetVolume(kNewVolume);
base::RunLoop().RunUntilIdle();
}
TEST_F(MojoAudioOutputStreamTest, Flush_FlushesStream) {
mojo::Remote<mojom::AudioOutputStream> audio_output_remote =
CreateAudioOutput();
EXPECT_CALL(client_, GotNotification());
EXPECT_CALL(*delegate_, OnFlushStream());
delegate_event_handler_->OnStreamCreated(kStreamId, std::move(mem_),
std::move(foreign_socket_));
audio_output_remote->Flush();
base::RunLoop().RunUntilIdle();
}
TEST_F(MojoAudioOutputStreamTest, DestructWithCallPending_Safe) {
mojo::Remote<mojom::AudioOutputStream> audio_output_remote =
CreateAudioOutput();
EXPECT_CALL(client_, GotNotification());
base::RunLoop().RunUntilIdle();
ASSERT_NE(nullptr, delegate_event_handler_);
foreign_socket_->ExpectOwnershipTransfer();
delegate_event_handler_->OnStreamCreated(kStreamId, std::move(mem_),
std::move(foreign_socket_));
audio_output_remote->Play();
impl_.reset();
base::RunLoop().RunUntilIdle();
}
TEST_F(MojoAudioOutputStreamTest, Created_NotifiesClient) {
mojo::Remote<mojom::AudioOutputStream> audio_output_remote =
CreateAudioOutput();
base::RunLoop().RunUntilIdle();
EXPECT_CALL(client_, GotNotification());
ASSERT_NE(nullptr, delegate_event_handler_);
foreign_socket_->ExpectOwnershipTransfer();
delegate_event_handler_->OnStreamCreated(kStreamId, std::move(mem_),
std::move(foreign_socket_));
base::RunLoop().RunUntilIdle();
}
TEST_F(MojoAudioOutputStreamTest, SetVolumeTooLarge_Error) {
mojo::Remote<mojom::AudioOutputStream> audio_output_remote =
CreateAudioOutput();
EXPECT_CALL(deleter_, Finished(true));
EXPECT_CALL(client_, GotNotification());
delegate_event_handler_->OnStreamCreated(kStreamId, std::move(mem_),
std::move(foreign_socket_));
audio_output_remote->SetVolume(15);
base::RunLoop().RunUntilIdle();
Mock::VerifyAndClear(&deleter_);
}
TEST_F(MojoAudioOutputStreamTest, SetVolumeNegative_Error) {
mojo::Remote<mojom::AudioOutputStream> audio_output_remote =
CreateAudioOutput();
EXPECT_CALL(deleter_, Finished(true));
EXPECT_CALL(client_, GotNotification());
delegate_event_handler_->OnStreamCreated(kStreamId, std::move(mem_),
std::move(foreign_socket_));
audio_output_remote->SetVolume(-0.5);
base::RunLoop().RunUntilIdle();
Mock::VerifyAndClear(&deleter_);
}
TEST_F(MojoAudioOutputStreamTest, DelegateErrorBeforeCreated_PropagatesError) {
mojo::Remote<mojom::AudioOutputStream> audio_output_remote =
CreateAudioOutput();
EXPECT_CALL(deleter_, Finished(true));
ASSERT_NE(nullptr, delegate_event_handler_);
delegate_event_handler_->OnStreamError(kStreamId);
base::RunLoop().RunUntilIdle();
Mock::VerifyAndClear(&deleter_);
}
TEST_F(MojoAudioOutputStreamTest, DelegateErrorAfterCreated_PropagatesError) {
mojo::Remote<mojom::AudioOutputStream> audio_output_remote =
CreateAudioOutput();
EXPECT_CALL(client_, GotNotification());
EXPECT_CALL(deleter_, Finished(true));
base::RunLoop().RunUntilIdle();
ASSERT_NE(nullptr, delegate_event_handler_);
foreign_socket_->ExpectOwnershipTransfer();
delegate_event_handler_->OnStreamCreated(kStreamId, std::move(mem_),
std::move(foreign_socket_));
delegate_event_handler_->OnStreamError(kStreamId);
base::RunLoop().RunUntilIdle();
Mock::VerifyAndClear(&deleter_);
}
TEST_F(MojoAudioOutputStreamTest, RemoteEndGone_CallsDeleter) {
mojo::Remote<mojom::AudioOutputStream> audio_output_remote =
CreateAudioOutput();
EXPECT_CALL(client_, GotNotification());
EXPECT_CALL(deleter_, Finished(false));
delegate_event_handler_->OnStreamCreated(kStreamId, std::move(mem_),
std::move(foreign_socket_));
audio_output_remote.reset();
base::RunLoop().RunUntilIdle();
Mock::VerifyAndClear(&deleter_);
}
} // namespace media