// 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()); 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) { } ~AudioOutputControllerTest() override {} 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 dest = AudioBus::Create(params_); ASSERT_TRUE(mock_stream_.callback()); const int frames_read = mock_stream_.callback()->OnMoreData(dest.get(), 0); 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(AudioManager::GetDefaultDeviceName(), base::Bind(&base::DoNothing)); } void Close() { EXPECT_CALL(mock_sync_reader_, Close()); controller_->Close(base::MessageLoop::QuitWhenIdleClosure()); 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 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 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