audio_output_device_unittest.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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/message_loop.h" 9#include "base/process_util.h" 10#include "base/shared_memory.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 "media/audio/shared_memory_util.h" 16#include "testing/gmock/include/gmock/gmock.h" 17#include "testing/gmock_mutant.h" 18#include "testing/gtest/include/gtest/gtest.h" 19 20using base::CancelableSyncSocket; 21using base::SharedMemory; 22using base::SyncSocket; 23using testing::_; 24using testing::DoAll; 25using testing::Invoke; 26using testing::Return; 27using testing::WithArgs; 28using testing::StrictMock; 29using testing::Values; 30 31namespace media { 32 33namespace { 34 35class MockRenderCallback : public AudioRendererSink::RenderCallback { 36 public: 37 MockRenderCallback() {} 38 virtual ~MockRenderCallback() {} 39 40 MOCK_METHOD2(Render, int(AudioBus* dest, int audio_delay_milliseconds)); 41 MOCK_METHOD3(RenderIO, void(AudioBus* source, 42 AudioBus* dest, 43 int audio_delay_milliseconds)); 44 MOCK_METHOD0(OnRenderError, void()); 45}; 46 47class MockAudioOutputIPC : public AudioOutputIPC { 48 public: 49 MockAudioOutputIPC() {} 50 virtual ~MockAudioOutputIPC() {} 51 52 MOCK_METHOD2(CreateStream, void(AudioOutputIPCDelegate* delegate, 53 const AudioParameters& params)); 54 MOCK_METHOD0(PlayStream, void()); 55 MOCK_METHOD0(PauseStream, void()); 56 MOCK_METHOD0(CloseStream, void()); 57 MOCK_METHOD1(SetVolume, void(double volume)); 58}; 59 60// Creates a copy of a SyncSocket handle that we can give to AudioOutputDevice. 61// On Windows this means duplicating the pipe handle so that AudioOutputDevice 62// can call CloseHandle() (since ownership has been transferred), but on other 63// platforms, we just copy the same socket handle since AudioOutputDevice on 64// those platforms won't actually own the socket (FileDescriptor.auto_close is 65// false). 66bool DuplicateSocketHandle(SyncSocket::Handle socket_handle, 67 SyncSocket::Handle* copy) { 68#if defined(OS_WIN) 69 HANDLE process = GetCurrentProcess(); 70 ::DuplicateHandle(process, socket_handle, process, copy, 71 0, FALSE, DUPLICATE_SAME_ACCESS); 72 return *copy != NULL; 73#else 74 *copy = socket_handle; 75 return *copy != -1; 76#endif 77} 78 79ACTION_P2(SendPendingBytes, socket, pending_bytes) { 80 socket->Send(&pending_bytes, sizeof(pending_bytes)); 81} 82 83// Used to terminate a loop from a different thread than the loop belongs to. 84// |loop| should be a MessageLoopProxy. 85ACTION_P(QuitLoop, loop) { 86 loop->PostTask(FROM_HERE, base::MessageLoop::QuitClosure()); 87} 88 89} // namespace. 90 91class AudioOutputDeviceTest 92 : public testing::Test, 93 public testing::WithParamInterface<bool> { 94 public: 95 AudioOutputDeviceTest(); 96 ~AudioOutputDeviceTest(); 97 98 void StartAudioDevice(); 99 void CreateStream(); 100 void ExpectRenderCallback(); 101 void WaitUntilRenderCallback(); 102 void StopAudioDevice(); 103 104 protected: 105 // Used to clean up TLS pointers that the test(s) will initialize. 106 // Must remain the first member of this class. 107 base::ShadowingAtExitManager at_exit_manager_; 108 base::MessageLoopForIO io_loop_; 109 AudioParameters default_audio_parameters_; 110 StrictMock<MockRenderCallback> callback_; 111 MockAudioOutputIPC* audio_output_ipc_; // owned by audio_device_ 112 scoped_refptr<AudioOutputDevice> audio_device_; 113 114 private: 115 int CalculateMemorySize(); 116 117 const bool synchronized_io_; 118 const int input_channels_; 119 SharedMemory shared_memory_; 120 CancelableSyncSocket browser_socket_; 121 CancelableSyncSocket renderer_socket_; 122 123 DISALLOW_COPY_AND_ASSIGN(AudioOutputDeviceTest); 124}; 125 126static const int kStreamId = 123; 127 128int AudioOutputDeviceTest::CalculateMemorySize() { 129 // Calculate output and input memory size. 130 int output_memory_size = 131 AudioBus::CalculateMemorySize(default_audio_parameters_); 132 133 int frames = default_audio_parameters_.frames_per_buffer(); 134 int input_memory_size = 135 AudioBus::CalculateMemorySize(input_channels_, frames); 136 137 int io_buffer_size = output_memory_size + input_memory_size; 138 139 // This is where it gets a bit hacky. The shared memory contract between 140 // AudioOutputDevice and its browser side counter part includes a bit more 141 // than just the audio data, so we must call TotalSharedMemorySizeInBytes() 142 // to get the actual size needed to fit the audio data plus the extra data. 143 return TotalSharedMemorySizeInBytes(io_buffer_size); 144} 145 146AudioOutputDeviceTest::AudioOutputDeviceTest() 147 : synchronized_io_(GetParam()), 148 input_channels_(synchronized_io_ ? 2 : 0) { 149 default_audio_parameters_.Reset( 150 AudioParameters::AUDIO_PCM_LINEAR, 151 CHANNEL_LAYOUT_STEREO, 2, input_channels_, 152 48000, 16, 1024); 153 154 audio_output_ipc_ = new MockAudioOutputIPC(); 155 audio_device_ = new AudioOutputDevice( 156 scoped_ptr<AudioOutputIPC>(audio_output_ipc_), 157 io_loop_.message_loop_proxy()); 158 159 audio_device_->Initialize(default_audio_parameters_, 160 &callback_); 161 162 io_loop_.RunUntilIdle(); 163} 164 165AudioOutputDeviceTest::~AudioOutputDeviceTest() { 166 audio_device_ = NULL; 167} 168 169void AudioOutputDeviceTest::StartAudioDevice() { 170 audio_device_->Start(); 171 172 EXPECT_CALL(*audio_output_ipc_, CreateStream(audio_device_.get(), _)); 173 174 io_loop_.RunUntilIdle(); 175} 176 177void AudioOutputDeviceTest::CreateStream() { 178 const int kMemorySize = CalculateMemorySize(); 179 180 ASSERT_TRUE(shared_memory_.CreateAndMapAnonymous(kMemorySize)); 181 memset(shared_memory_.memory(), 0xff, kMemorySize); 182 183 ASSERT_TRUE(CancelableSyncSocket::CreatePair(&browser_socket_, 184 &renderer_socket_)); 185 186 // Create duplicates of the handles we pass to AudioOutputDevice since 187 // ownership will be transferred and AudioOutputDevice is responsible for 188 // freeing. 189 SyncSocket::Handle audio_device_socket = SyncSocket::kInvalidHandle; 190 ASSERT_TRUE(DuplicateSocketHandle(renderer_socket_.handle(), 191 &audio_device_socket)); 192 base::SharedMemoryHandle duplicated_memory_handle; 193 ASSERT_TRUE(shared_memory_.ShareToProcess(base::GetCurrentProcessHandle(), 194 &duplicated_memory_handle)); 195 196 audio_device_->OnStreamCreated(duplicated_memory_handle, audio_device_socket, 197 PacketSizeInBytes(kMemorySize)); 198 io_loop_.RunUntilIdle(); 199} 200 201void AudioOutputDeviceTest::ExpectRenderCallback() { 202 // We should get a 'play' notification when we call OnStreamCreated(). 203 // Respond by asking for some audio data. This should ask our callback 204 // to provide some audio data that AudioOutputDevice then writes into the 205 // shared memory section. 206 const int kMemorySize = CalculateMemorySize(); 207 208 EXPECT_CALL(*audio_output_ipc_, PlayStream()) 209 .WillOnce(SendPendingBytes(&browser_socket_, kMemorySize)); 210 211 // We expect calls to our audio renderer callback, which returns the number 212 // of frames written to the memory section. 213 // Here's the second place where it gets hacky: There's no way for us to 214 // know (without using a sleep loop!) when the AudioOutputDevice has finished 215 // writing the interleaved audio data into the shared memory section. 216 // So, for the sake of this test, we consider the call to Render a sign 217 // of success and quit the loop. 218 if (synchronized_io_) { 219 // For synchronized I/O, we expect RenderIO(). 220 EXPECT_CALL(callback_, RenderIO(_, _, _)) 221 .WillOnce(QuitLoop(io_loop_.message_loop_proxy())); 222 } else { 223 // For output only we expect Render(). 224 const int kNumberOfFramesToProcess = 0; 225 EXPECT_CALL(callback_, Render(_, _)) 226 .WillOnce(DoAll( 227 QuitLoop(io_loop_.message_loop_proxy()), 228 Return(kNumberOfFramesToProcess))); 229 } 230} 231 232void AudioOutputDeviceTest::WaitUntilRenderCallback() { 233 // Don't hang the test if we never get the Render() callback. 234 io_loop_.PostDelayedTask(FROM_HERE, base::MessageLoop::QuitClosure(), 235 TestTimeouts::action_timeout()); 236 io_loop_.Run(); 237} 238 239void AudioOutputDeviceTest::StopAudioDevice() { 240 audio_device_->Stop(); 241 242 EXPECT_CALL(*audio_output_ipc_, CloseStream()); 243 244 io_loop_.RunUntilIdle(); 245} 246 247TEST_P(AudioOutputDeviceTest, Initialize) { 248 // Tests that the object can be constructed, initialized and destructed 249 // without having ever been started/stopped. 250} 251 252// Calls Start() followed by an immediate Stop() and check for the basic message 253// filter messages being sent in that case. 254TEST_P(AudioOutputDeviceTest, StartStop) { 255 StartAudioDevice(); 256 StopAudioDevice(); 257} 258 259// AudioOutputDevice supports multiple start/stop sequences. 260TEST_P(AudioOutputDeviceTest, StartStopStartStop) { 261 StartAudioDevice(); 262 StopAudioDevice(); 263 StartAudioDevice(); 264 StopAudioDevice(); 265} 266 267// Simulate receiving OnStreamCreated() prior to processing ShutDownOnIOThread() 268// on the IO loop. 269TEST_P(AudioOutputDeviceTest, StopBeforeRender) { 270 StartAudioDevice(); 271 272 // Call Stop() but don't run the IO loop yet. 273 audio_device_->Stop(); 274 275 // Expect us to shutdown IPC but not to render anything despite the stream 276 // getting created. 277 EXPECT_CALL(*audio_output_ipc_, CloseStream()); 278 CreateStream(); 279} 280 281// Full test with output only. 282TEST_P(AudioOutputDeviceTest, CreateStream) { 283 StartAudioDevice(); 284 ExpectRenderCallback(); 285 CreateStream(); 286 WaitUntilRenderCallback(); 287 StopAudioDevice(); 288} 289 290INSTANTIATE_TEST_CASE_P(Render, AudioOutputDeviceTest, Values(false)); 291INSTANTIATE_TEST_CASE_P(RenderIO, AudioOutputDeviceTest, Values(true)); 292 293} // namespace media. 294