// 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 "base/basictypes.h"
#include "base/bind.h"
#include "base/environment.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/synchronization/waitable_event.h"
#include "media/audio/audio_manager_base.h"
#include "media/audio/audio_output_controller.h"
#include "media/audio/audio_parameters.h"
#include "media/base/audio_bus.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
using ::testing::AtLeast;
using ::testing::DoAll;
using ::testing::Invoke;
using ::testing::NotNull;
using ::testing::Return;
namespace media {
static const int kSampleRate = AudioParameters::kAudioCDSampleRate;
static const int kBitsPerSample = 16;
static const ChannelLayout kChannelLayout = CHANNEL_LAYOUT_STEREO;
static const int kSamplesPerPacket = kSampleRate / 100;
static const double kTestVolume = 0.25;
class MockAudioOutputControllerEventHandler
: public AudioOutputController::EventHandler {
public:
MockAudioOutputControllerEventHandler() {}
MOCK_METHOD0(OnCreated, void());
MOCK_METHOD0(OnPlaying, void());
MOCK_METHOD0(OnPaused, void());
MOCK_METHOD0(OnError, void());
MOCK_METHOD2(OnDeviceChange, void(int new_buffer_size, int new_sample_rate));
private:
DISALLOW_COPY_AND_ASSIGN(MockAudioOutputControllerEventHandler);
};
class MockAudioOutputControllerSyncReader
: public AudioOutputController::SyncReader {
public:
MockAudioOutputControllerSyncReader() {}
MOCK_METHOD1(UpdatePendingBytes, void(uint32 bytes));
MOCK_METHOD1(Read, void(AudioBus* dest));
MOCK_METHOD0(Close, void());
private:
DISALLOW_COPY_AND_ASSIGN(MockAudioOutputControllerSyncReader);
};
class MockAudioOutputStream : public AudioOutputStream {
public:
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());
// Set/get the callback passed to Start().
AudioSourceCallback* callback() const { return callback_; }
void SetCallback(AudioSourceCallback* asc) { callback_ = asc; }
private:
AudioSourceCallback* callback_;
};
ACTION_P(SignalEvent, event) {
event->Signal();
}
static const float kBufferNonZeroData = 1.0f;
ACTION(PopulateBuffer) {
arg0->Zero();
// Note: To confirm the buffer will be populated in these tests, it's
// sufficient that only the first float in channel 0 is set to the value.
arg0->channel(0)[0] = kBufferNonZeroData;
}
class AudioOutputControllerTest : public testing::Test {
public:
AudioOutputControllerTest()
: audio_manager_(AudioManager::CreateForTesting()),
create_event_(false, false),
play_event_(false, false),
read_event_(false, false),
pause_event_(false, false) {
}
virtual ~AudioOutputControllerTest() {
}
protected:
void Create(int samples_per_packet) {
EXPECT_FALSE(create_event_.IsSignaled());
EXPECT_FALSE(play_event_.IsSignaled());
EXPECT_FALSE(read_event_.IsSignaled());
EXPECT_FALSE(pause_event_.IsSignaled());
params_ = AudioParameters(
AudioParameters::AUDIO_FAKE, kChannelLayout,
kSampleRate, kBitsPerSample, samples_per_packet);
if (params_.IsValid()) {
EXPECT_CALL(mock_event_handler_, OnCreated())
.WillOnce(SignalEvent(&create_event_));
}
controller_ = AudioOutputController::Create(
audio_manager_.get(), &mock_event_handler_, params_, std::string(),
&mock_sync_reader_);
if (controller_.get())
controller_->SetVolume(kTestVolume);
EXPECT_EQ(params_.IsValid(), controller_.get() != NULL);
}
void Play() {
// Expect the event handler to receive one OnPlaying() call.
EXPECT_CALL(mock_event_handler_, OnPlaying())
.WillOnce(SignalEvent(&play_event_));
// During playback, the mock pretends to provide audio data rendered and
// sent from the render process.
EXPECT_CALL(mock_sync_reader_, UpdatePendingBytes(_))
.Times(AtLeast(1));
EXPECT_CALL(mock_sync_reader_, Read(_))
.WillRepeatedly(DoAll(PopulateBuffer(),
SignalEvent(&read_event_)));
controller_->Play();
}
void Pause() {
// Expect the event handler to receive one OnPaused() call.
EXPECT_CALL(mock_event_handler_, OnPaused())
.WillOnce(SignalEvent(&pause_event_));
controller_->Pause();
}
void ChangeDevice() {
// Expect the event handler to receive one OnPaying() call and no OnPaused()
// call.
EXPECT_CALL(mock_event_handler_, OnPlaying())
.WillOnce(SignalEvent(&play_event_));
EXPECT_CALL(mock_event_handler_, OnPaused())
.Times(0);
// Simulate a device change event to AudioOutputController from the
// AudioManager.
audio_manager_->GetTaskRunner()->PostTask(
FROM_HERE,
base::Bind(&AudioOutputController::OnDeviceChange, controller_));
}
void Divert(bool was_playing, int num_times_to_be_started) {
if (was_playing) {
// Expect the handler to receive one OnPlaying() call as a result of the
// stream switching.
EXPECT_CALL(mock_event_handler_, OnPlaying())
.WillOnce(SignalEvent(&play_event_));
}
EXPECT_CALL(mock_stream_, Open())
.WillOnce(Return(true));
EXPECT_CALL(mock_stream_, SetVolume(kTestVolume));
if (num_times_to_be_started > 0) {
EXPECT_CALL(mock_stream_, Start(NotNull()))
.Times(num_times_to_be_started)
.WillRepeatedly(
Invoke(&mock_stream_, &MockAudioOutputStream::SetCallback));
EXPECT_CALL(mock_stream_, Stop())
.Times(num_times_to_be_started);
}
controller_->StartDiverting(&mock_stream_);
}
void ReadDivertedAudioData() {
scoped_ptr<AudioBus> dest = AudioBus::Create(params_);
ASSERT_TRUE(!!mock_stream_.callback());
const int frames_read =
mock_stream_.callback()->OnMoreData(dest.get(), AudioBuffersState());
EXPECT_LT(0, frames_read);
EXPECT_EQ(kBufferNonZeroData, dest->channel(0)[0]);
}
void Revert(bool was_playing) {
if (was_playing) {
// Expect the handler to receive one OnPlaying() call as a result of the
// stream switching back.
EXPECT_CALL(mock_event_handler_, OnPlaying())
.WillOnce(SignalEvent(&play_event_));
}
EXPECT_CALL(mock_stream_, Close());
controller_->StopDiverting();
}
void SwitchDevice(bool diverting) {
if (!diverting) {
// Expect the current stream to close and a new stream to start
// playing if not diverting. When diverting, nothing happens
// until diverting is stopped.
EXPECT_CALL(mock_event_handler_, OnPlaying())
.WillOnce(SignalEvent(&play_event_));
}
controller_->SwitchOutputDevice(AudioManagerBase::kDefaultDeviceName,
base::Bind(&base::DoNothing));
}
void Close() {
EXPECT_CALL(mock_sync_reader_, Close());
controller_->Close(base::MessageLoop::QuitClosure());
base::MessageLoop::current()->Run();
}
// These help make test sequences more readable.
void DivertNeverPlaying() { Divert(false, 0); }
void DivertWillEventuallyBeTwicePlayed() { Divert(false, 2); }
void DivertWhilePlaying() { Divert(true, 1); }
void RevertWasNotPlaying() { Revert(false); }
void RevertWhilePlaying() { Revert(true); }
// These synchronize the main thread with key events taking place on other
// threads.
void WaitForCreate() { create_event_.Wait(); }
void WaitForPlay() { play_event_.Wait(); }
void WaitForReads() {
// Note: Arbitrarily chosen, but more iterations causes tests to take
// significantly more time.
static const int kNumIterations = 3;
for (int i = 0; i < kNumIterations; ++i) {
read_event_.Wait();
}
}
void WaitForPause() { pause_event_.Wait(); }
private:
base::MessageLoopForIO message_loop_;
scoped_ptr<AudioManager> audio_manager_;
MockAudioOutputControllerEventHandler mock_event_handler_;
MockAudioOutputControllerSyncReader mock_sync_reader_;
MockAudioOutputStream mock_stream_;
base::WaitableEvent create_event_;
base::WaitableEvent play_event_;
base::WaitableEvent read_event_;
base::WaitableEvent pause_event_;
AudioParameters params_;
scoped_refptr<AudioOutputController> controller_;
DISALLOW_COPY_AND_ASSIGN(AudioOutputControllerTest);
};
TEST_F(AudioOutputControllerTest, CreateAndClose) {
Create(kSamplesPerPacket);
Close();
}
TEST_F(AudioOutputControllerTest, HardwareBufferTooLarge) {
Create(kSamplesPerPacket * 1000);
}
TEST_F(AudioOutputControllerTest, PlayAndClose) {
Create(kSamplesPerPacket);
WaitForCreate();
Play();
WaitForPlay();
WaitForReads();
Close();
}
TEST_F(AudioOutputControllerTest, PlayPauseClose) {
Create(kSamplesPerPacket);
WaitForCreate();
Play();
WaitForPlay();
WaitForReads();
Pause();
WaitForPause();
Close();
}
TEST_F(AudioOutputControllerTest, PlayPausePlayClose) {
Create(kSamplesPerPacket);
WaitForCreate();
Play();
WaitForPlay();
WaitForReads();
Pause();
WaitForPause();
Play();
WaitForPlay();
Close();
}
TEST_F(AudioOutputControllerTest, PlayDeviceChangeClose) {
Create(kSamplesPerPacket);
WaitForCreate();
Play();
WaitForPlay();
WaitForReads();
ChangeDevice();
WaitForPlay();
WaitForReads();
Close();
}
TEST_F(AudioOutputControllerTest, PlaySwitchDeviceClose) {
Create(kSamplesPerPacket);
WaitForCreate();
Play();
WaitForPlay();
WaitForReads();
SwitchDevice(false);
WaitForPlay();
WaitForReads();
Close();
}
TEST_F(AudioOutputControllerTest, PlayDivertRevertClose) {
Create(kSamplesPerPacket);
WaitForCreate();
Play();
WaitForPlay();
WaitForReads();
DivertWhilePlaying();
WaitForPlay();
ReadDivertedAudioData();
RevertWhilePlaying();
WaitForPlay();
WaitForReads();
Close();
}
TEST_F(AudioOutputControllerTest, PlayDivertSwitchDeviceRevertClose) {
Create(kSamplesPerPacket);
WaitForCreate();
Play();
WaitForPlay();
WaitForReads();
DivertWhilePlaying();
WaitForPlay();
SwitchDevice(true);
ReadDivertedAudioData();
RevertWhilePlaying();
WaitForPlay();
WaitForReads();
Close();
}
TEST_F(AudioOutputControllerTest, PlayDivertRevertDivertRevertClose) {
Create(kSamplesPerPacket);
WaitForCreate();
Play();
WaitForPlay();
WaitForReads();
DivertWhilePlaying();
WaitForPlay();
ReadDivertedAudioData();
RevertWhilePlaying();
WaitForPlay();
WaitForReads();
DivertWhilePlaying();
WaitForPlay();
ReadDivertedAudioData();
RevertWhilePlaying();
WaitForPlay();
WaitForReads();
Close();
}
TEST_F(AudioOutputControllerTest, DivertPlayPausePlayRevertClose) {
Create(kSamplesPerPacket);
WaitForCreate();
DivertWillEventuallyBeTwicePlayed();
Play();
WaitForPlay();
ReadDivertedAudioData();
Pause();
WaitForPause();
Play();
WaitForPlay();
ReadDivertedAudioData();
RevertWhilePlaying();
WaitForPlay();
WaitForReads();
Close();
}
TEST_F(AudioOutputControllerTest, DivertRevertClose) {
Create(kSamplesPerPacket);
WaitForCreate();
DivertNeverPlaying();
RevertWasNotPlaying();
Close();
}
} // namespace media