blob: 40a59a4ba7b0e988b3548bb70472230ea23970d6 [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/memory/shared_memory.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/sync_socket.h"
#include "media/audio/audio_output_controller.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;
using AudioOutputStream = mojom::AudioOutputStream;
using AudioOutputStreamPtr = mojo::InterfacePtr<AudioOutputStream>;
class TestCancelableSyncSocket : public base::CancelableSyncSocket {
public:
TestCancelableSyncSocket() {}
void ExpectOwnershipTransfer() { expect_ownership_transfer_ = true; }
~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_EQ(handle(), kInvalidHandle);
}
private:
bool expect_ownership_transfer_ = false;
DISALLOW_COPY_AND_ASSIGN(TestCancelableSyncSocket);
};
class MockDelegate : NON_EXPORTED_BASE(public AudioOutputDelegate) {
public:
MockDelegate() {}
~MockDelegate() {}
MOCK_CONST_METHOD0(GetStreamId, int());
MOCK_METHOD0(OnPlayStream, void());
MOCK_METHOD0(OnPauseStream, void());
MOCK_METHOD1(OnSetVolume, void(double));
};
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_METHOD0(Finished, void());
};
class MockClient {
public:
MockClient() {}
void Initialized(mojo::ScopedSharedBufferHandle shared_buffer,
mojo::ScopedHandle socket_handle) {
ASSERT_TRUE(shared_buffer.is_valid());
ASSERT_TRUE(socket_handle.is_valid());
base::PlatformFile fd;
mojo::UnwrapPlatformFile(std::move(socket_handle), &fd);
socket_ = base::MakeUnique<base::CancelableSyncSocket>(fd);
EXPECT_NE(socket_->handle(), base::CancelableSyncSocket::kInvalidHandle);
size_t memory_length;
base::SharedMemoryHandle shmem_handle;
bool read_only;
EXPECT_EQ(
mojo::UnwrapSharedMemoryHandle(std::move(shared_buffer), &shmem_handle,
&memory_length, &read_only),
MOJO_RESULT_OK);
EXPECT_FALSE(read_only);
buffer_ = base::MakeUnique<base::SharedMemory>(shmem_handle, read_only);
GotNotification();
}
MOCK_METHOD0(GotNotification, void());
private:
std::unique_ptr<base::SharedMemory> buffer_;
std::unique_ptr<base::CancelableSyncSocket> socket_;
};
std::unique_ptr<AudioOutputDelegate> CreateNoDelegate(
AudioOutputDelegate::EventHandler* event_handler) {
return nullptr;
}
void NotCalled(mojo::ScopedSharedBufferHandle shared_buffer,
mojo::ScopedHandle socket_handle) {
EXPECT_TRUE(false) << "The StreamCreated callback was called despite the "
"test expecting it not to.";
}
} // namespace
class MojoAudioOutputStreamTest : public Test {
public:
MojoAudioOutputStreamTest()
: foreign_socket_(base::MakeUnique<TestCancelableSyncSocket>()) {}
AudioOutputStreamPtr CreateAudioOutput() {
AudioOutputStreamPtr p;
ExpectDelegateCreation();
impl_ = base::MakeUnique<MojoAudioOutputStream>(
mojo::MakeRequest(&p),
base::BindOnce(&MockDelegateFactory::CreateDelegate,
base::Unretained(&mock_delegate_factory_)),
base::Bind(&MockClient::Initialized, base::Unretained(&client_)),
base::BindOnce(&MockDeleter::Finished, base::Unretained(&deleter_)));
EXPECT_TRUE(p.is_bound());
return p;
}
protected:
void ExpectDelegateCreation() {
delegate_ = new StrictMock<MockDelegate>();
mock_delegate_factory_.PrepareDelegateForCreation(
base::WrapUnique(delegate_));
EXPECT_TRUE(
base::CancelableSyncSocket::CreatePair(&local_, foreign_socket_.get()));
EXPECT_TRUE(mem_.CreateAnonymous(kShmemSize));
EXPECT_CALL(mock_delegate_factory_, MockCreateDelegate(NotNull()))
.WillOnce(SaveArg<0>(&delegate_event_handler_));
}
base::MessageLoop loop_;
base::CancelableSyncSocket local_;
std::unique_ptr<TestCancelableSyncSocket> foreign_socket_;
base::SharedMemory mem_;
StrictMock<MockDelegate>* delegate_ = nullptr;
AudioOutputDelegate::EventHandler* delegate_event_handler_ = nullptr;
StrictMock<MockDelegateFactory> mock_delegate_factory_;
StrictMock<MockDeleter> deleter_;
MockClient client_;
std::unique_ptr<MojoAudioOutputStream> impl_;
};
TEST_F(MojoAudioOutputStreamTest, NoDelegate_SignalsError) {
bool deleter_called = false;
mojom::AudioOutputStreamPtr stream_ptr;
MojoAudioOutputStream stream(
mojo::MakeRequest(&stream_ptr), base::BindOnce(&CreateNoDelegate),
base::Bind(&NotCalled),
base::BindOnce([](bool* p) { *p = true; }, &deleter_called));
EXPECT_FALSE(deleter_called)
<< "Stream shouldn't call the deleter from its constructor.";
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(deleter_called);
}
TEST_F(MojoAudioOutputStreamTest, Play_Plays) {
AudioOutputStreamPtr audio_output_ptr = CreateAudioOutput();
EXPECT_CALL(*delegate_, OnPlayStream());
audio_output_ptr->Play();
base::RunLoop().RunUntilIdle();
}
TEST_F(MojoAudioOutputStreamTest, Pause_Pauses) {
AudioOutputStreamPtr audio_output_ptr = CreateAudioOutput();
EXPECT_CALL(*delegate_, OnPauseStream());
audio_output_ptr->Pause();
base::RunLoop().RunUntilIdle();
}
TEST_F(MojoAudioOutputStreamTest, SetVolume_SetsVolume) {
AudioOutputStreamPtr audio_output_ptr = CreateAudioOutput();
EXPECT_CALL(*delegate_, OnSetVolume(kNewVolume));
audio_output_ptr->SetVolume(kNewVolume);
base::RunLoop().RunUntilIdle();
}
TEST_F(MojoAudioOutputStreamTest, DestructWithCallPending_Safe) {
AudioOutputStreamPtr audio_output_ptr = CreateAudioOutput();
base::RunLoop().RunUntilIdle();
ASSERT_NE(nullptr, delegate_event_handler_);
foreign_socket_->ExpectOwnershipTransfer();
delegate_event_handler_->OnStreamCreated(kStreamId, &mem_,
std::move(foreign_socket_));
audio_output_ptr->Play();
impl_.reset();
base::RunLoop().RunUntilIdle();
}
TEST_F(MojoAudioOutputStreamTest, Created_NotifiesClient) {
AudioOutputStreamPtr audio_output_ptr = CreateAudioOutput();
base::RunLoop().RunUntilIdle();
EXPECT_CALL(client_, GotNotification());
ASSERT_NE(nullptr, delegate_event_handler_);
foreign_socket_->ExpectOwnershipTransfer();
delegate_event_handler_->OnStreamCreated(kStreamId, &mem_,
std::move(foreign_socket_));
base::RunLoop().RunUntilIdle();
}
TEST_F(MojoAudioOutputStreamTest, SetVolumeTooLarge_Error) {
AudioOutputStreamPtr audio_output_ptr = CreateAudioOutput();
EXPECT_CALL(deleter_, Finished());
audio_output_ptr->SetVolume(15);
base::RunLoop().RunUntilIdle();
Mock::VerifyAndClear(&deleter_);
}
TEST_F(MojoAudioOutputStreamTest, SetVolumeNegative_Error) {
AudioOutputStreamPtr audio_output_ptr = CreateAudioOutput();
EXPECT_CALL(deleter_, Finished());
audio_output_ptr->SetVolume(-0.5);
base::RunLoop().RunUntilIdle();
Mock::VerifyAndClear(&deleter_);
}
TEST_F(MojoAudioOutputStreamTest, DelegateErrorBeforeCreated_PropagatesError) {
AudioOutputStreamPtr audio_output_ptr = CreateAudioOutput();
EXPECT_CALL(deleter_, Finished());
ASSERT_NE(nullptr, delegate_event_handler_);
delegate_event_handler_->OnStreamError(kStreamId);
base::RunLoop().RunUntilIdle();
Mock::VerifyAndClear(&deleter_);
}
TEST_F(MojoAudioOutputStreamTest, DelegateErrorAfterCreated_PropagatesError) {
AudioOutputStreamPtr audio_output_ptr = CreateAudioOutput();
EXPECT_CALL(deleter_, Finished());
base::RunLoop().RunUntilIdle();
ASSERT_NE(nullptr, delegate_event_handler_);
foreign_socket_->ExpectOwnershipTransfer();
delegate_event_handler_->OnStreamCreated(kStreamId, &mem_,
std::move(foreign_socket_));
delegate_event_handler_->OnStreamError(kStreamId);
base::RunLoop().RunUntilIdle();
Mock::VerifyAndClear(&deleter_);
}
TEST_F(MojoAudioOutputStreamTest, RemoteEndGone_Error) {
AudioOutputStreamPtr audio_output_ptr = CreateAudioOutput();
EXPECT_CALL(deleter_, Finished());
audio_output_ptr.reset();
base::RunLoop().RunUntilIdle();
Mock::VerifyAndClear(&deleter_);
}
} // namespace media