1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <vector> 6 7#include "base/at_exit.h" 8#include "base/memory/shared_memory.h" 9#include "base/message_loop/message_loop.h" 10#include "base/process/process_handle.h" 11#include "base/sync_socket.h" 12#include "base/test/test_timeouts.h" 13#include "media/audio/audio_output_device.h" 14#include "media/audio/sample_rates.h" 15#include "testing/gmock/include/gmock/gmock.h" 16#include "testing/gmock_mutant.h" 17#include "testing/gtest/include/gtest/gtest.h" 18 19using base::CancelableSyncSocket; 20using base::SharedMemory; 21using base::SyncSocket; 22using testing::_; 23using testing::DoAll; 24using testing::Invoke; 25using testing::Return; 26using testing::WithArgs; 27using testing::StrictMock; 28using testing::Values; 29 30namespace media { 31 32namespace { 33 34class MockRenderCallback : public AudioRendererSink::RenderCallback { 35 public: 36 MockRenderCallback() {} 37 virtual ~MockRenderCallback() {} 38 39 MOCK_METHOD2(Render, int(AudioBus* dest, int audio_delay_milliseconds)); 40 MOCK_METHOD0(OnRenderError, void()); 41}; 42 43class MockAudioOutputIPC : public AudioOutputIPC { 44 public: 45 MockAudioOutputIPC() {} 46 virtual ~MockAudioOutputIPC() {} 47 48 MOCK_METHOD3(CreateStream, void(AudioOutputIPCDelegate* delegate, 49 const AudioParameters& params, 50 int session_id)); 51 MOCK_METHOD0(PlayStream, void()); 52 MOCK_METHOD0(PauseStream, void()); 53 MOCK_METHOD0(CloseStream, void()); 54 MOCK_METHOD1(SetVolume, void(double volume)); 55}; 56 57ACTION_P2(SendPendingBytes, socket, pending_bytes) { 58 socket->Send(&pending_bytes, sizeof(pending_bytes)); 59} 60 61// Used to terminate a loop from a different thread than the loop belongs to. 62// |loop| should be a MessageLoopProxy. 63ACTION_P(QuitLoop, loop) { 64 loop->PostTask(FROM_HERE, base::MessageLoop::QuitClosure()); 65} 66 67} // namespace. 68 69class AudioOutputDeviceTest 70 : public testing::Test, 71 public testing::WithParamInterface<bool> { 72 public: 73 AudioOutputDeviceTest(); 74 ~AudioOutputDeviceTest(); 75 76 void StartAudioDevice(); 77 void CreateStream(); 78 void ExpectRenderCallback(); 79 void WaitUntilRenderCallback(); 80 void StopAudioDevice(); 81 82 protected: 83 // Used to clean up TLS pointers that the test(s) will initialize. 84 // Must remain the first member of this class. 85 base::ShadowingAtExitManager at_exit_manager_; 86 base::MessageLoopForIO io_loop_; 87 AudioParameters default_audio_parameters_; 88 StrictMock<MockRenderCallback> callback_; 89 MockAudioOutputIPC* audio_output_ipc_; // owned by audio_device_ 90 scoped_refptr<AudioOutputDevice> audio_device_; 91 92 private: 93 int CalculateMemorySize(); 94 95 SharedMemory shared_memory_; 96 CancelableSyncSocket browser_socket_; 97 CancelableSyncSocket renderer_socket_; 98 99 DISALLOW_COPY_AND_ASSIGN(AudioOutputDeviceTest); 100}; 101 102int AudioOutputDeviceTest::CalculateMemorySize() { 103 // Calculate output memory size. 104 return AudioBus::CalculateMemorySize(default_audio_parameters_); 105} 106 107AudioOutputDeviceTest::AudioOutputDeviceTest() { 108 default_audio_parameters_.Reset( 109 AudioParameters::AUDIO_PCM_LINEAR, 110 CHANNEL_LAYOUT_STEREO, 2, 48000, 16, 1024); 111 112 audio_output_ipc_ = new MockAudioOutputIPC(); 113 audio_device_ = new AudioOutputDevice( 114 scoped_ptr<AudioOutputIPC>(audio_output_ipc_), 115 io_loop_.message_loop_proxy()); 116 117 audio_device_->Initialize(default_audio_parameters_, 118 &callback_); 119 120 io_loop_.RunUntilIdle(); 121} 122 123AudioOutputDeviceTest::~AudioOutputDeviceTest() { 124 audio_device_ = NULL; 125} 126 127void AudioOutputDeviceTest::StartAudioDevice() { 128 audio_device_->Start(); 129 130 EXPECT_CALL(*audio_output_ipc_, CreateStream(audio_device_.get(), _, 0)); 131 132 io_loop_.RunUntilIdle(); 133} 134 135void AudioOutputDeviceTest::CreateStream() { 136 const int kMemorySize = CalculateMemorySize(); 137 138 ASSERT_TRUE(shared_memory_.CreateAndMapAnonymous(kMemorySize)); 139 memset(shared_memory_.memory(), 0xff, kMemorySize); 140 141 ASSERT_TRUE(CancelableSyncSocket::CreatePair(&browser_socket_, 142 &renderer_socket_)); 143 144 // Create duplicates of the handles we pass to AudioOutputDevice since 145 // ownership will be transferred and AudioOutputDevice is responsible for 146 // freeing. 147 SyncSocket::TransitDescriptor audio_device_socket_descriptor; 148 ASSERT_TRUE(renderer_socket_.PrepareTransitDescriptor( 149 base::GetCurrentProcessHandle(), &audio_device_socket_descriptor)); 150 base::SharedMemoryHandle duplicated_memory_handle; 151 ASSERT_TRUE(shared_memory_.ShareToProcess(base::GetCurrentProcessHandle(), 152 &duplicated_memory_handle)); 153 154 audio_device_->OnStreamCreated( 155 duplicated_memory_handle, 156 SyncSocket::UnwrapHandle(audio_device_socket_descriptor), kMemorySize); 157 io_loop_.RunUntilIdle(); 158} 159 160void AudioOutputDeviceTest::ExpectRenderCallback() { 161 // We should get a 'play' notification when we call OnStreamCreated(). 162 // Respond by asking for some audio data. This should ask our callback 163 // to provide some audio data that AudioOutputDevice then writes into the 164 // shared memory section. 165 const int kMemorySize = CalculateMemorySize(); 166 167 EXPECT_CALL(*audio_output_ipc_, PlayStream()) 168 .WillOnce(SendPendingBytes(&browser_socket_, kMemorySize)); 169 170 // We expect calls to our audio renderer callback, which returns the number 171 // of frames written to the memory section. 172 // Here's the second place where it gets hacky: There's no way for us to 173 // know (without using a sleep loop!) when the AudioOutputDevice has finished 174 // writing the interleaved audio data into the shared memory section. 175 // So, for the sake of this test, we consider the call to Render a sign 176 // of success and quit the loop. 177 const int kNumberOfFramesToProcess = 0; 178 EXPECT_CALL(callback_, Render(_, _)) 179 .WillOnce(DoAll( 180 QuitLoop(io_loop_.message_loop_proxy()), 181 Return(kNumberOfFramesToProcess))); 182} 183 184void AudioOutputDeviceTest::WaitUntilRenderCallback() { 185 // Don't hang the test if we never get the Render() callback. 186 io_loop_.PostDelayedTask(FROM_HERE, base::MessageLoop::QuitClosure(), 187 TestTimeouts::action_timeout()); 188 io_loop_.Run(); 189} 190 191void AudioOutputDeviceTest::StopAudioDevice() { 192 audio_device_->Stop(); 193 194 EXPECT_CALL(*audio_output_ipc_, CloseStream()); 195 196 io_loop_.RunUntilIdle(); 197} 198 199TEST_P(AudioOutputDeviceTest, Initialize) { 200 // Tests that the object can be constructed, initialized and destructed 201 // without having ever been started/stopped. 202} 203 204// Calls Start() followed by an immediate Stop() and check for the basic message 205// filter messages being sent in that case. 206TEST_P(AudioOutputDeviceTest, StartStop) { 207 StartAudioDevice(); 208 StopAudioDevice(); 209} 210 211// AudioOutputDevice supports multiple start/stop sequences. 212TEST_P(AudioOutputDeviceTest, StartStopStartStop) { 213 StartAudioDevice(); 214 StopAudioDevice(); 215 StartAudioDevice(); 216 StopAudioDevice(); 217} 218 219// Simulate receiving OnStreamCreated() prior to processing ShutDownOnIOThread() 220// on the IO loop. 221TEST_P(AudioOutputDeviceTest, StopBeforeRender) { 222 StartAudioDevice(); 223 224 // Call Stop() but don't run the IO loop yet. 225 audio_device_->Stop(); 226 227 // Expect us to shutdown IPC but not to render anything despite the stream 228 // getting created. 229 EXPECT_CALL(*audio_output_ipc_, CloseStream()); 230 CreateStream(); 231} 232 233// Full test with output only. 234TEST_P(AudioOutputDeviceTest, CreateStream) { 235 StartAudioDevice(); 236 ExpectRenderCallback(); 237 CreateStream(); 238 WaitUntilRenderCallback(); 239 StopAudioDevice(); 240} 241 242INSTANTIATE_TEST_CASE_P(Render, AudioOutputDeviceTest, Values(false)); 243 244} // namespace media. 245