audio_output_device_unittest.cc revision 1e9bf3e0803691d0a228da41fc608347b6db4340
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_METHOD3(RenderIO, void(AudioBus* source, 41 AudioBus* dest, 42 int audio_delay_milliseconds)); 43 MOCK_METHOD0(OnRenderError, void()); 44}; 45 46class MockAudioOutputIPC : public AudioOutputIPC { 47 public: 48 MockAudioOutputIPC() {} 49 virtual ~MockAudioOutputIPC() {} 50 51 MOCK_METHOD3(CreateStream, void(AudioOutputIPCDelegate* delegate, 52 const AudioParameters& params, 53 int session_id)); 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 126int AudioOutputDeviceTest::CalculateMemorySize() { 127 // Calculate output and input memory size. 128 int output_memory_size = 129 AudioBus::CalculateMemorySize(default_audio_parameters_); 130 131 int frames = default_audio_parameters_.frames_per_buffer(); 132 int input_memory_size = 133 AudioBus::CalculateMemorySize(input_channels_, frames); 134 135 return output_memory_size + input_memory_size; 136} 137 138AudioOutputDeviceTest::AudioOutputDeviceTest() 139 : synchronized_io_(GetParam()), 140 input_channels_(synchronized_io_ ? 2 : 0) { 141 default_audio_parameters_.Reset( 142 AudioParameters::AUDIO_PCM_LINEAR, 143 CHANNEL_LAYOUT_STEREO, 2, input_channels_, 144 48000, 16, 1024); 145 146 audio_output_ipc_ = new MockAudioOutputIPC(); 147 audio_device_ = new AudioOutputDevice( 148 scoped_ptr<AudioOutputIPC>(audio_output_ipc_), 149 io_loop_.message_loop_proxy()); 150 151 audio_device_->Initialize(default_audio_parameters_, 152 &callback_); 153 154 io_loop_.RunUntilIdle(); 155} 156 157AudioOutputDeviceTest::~AudioOutputDeviceTest() { 158 audio_device_ = NULL; 159} 160 161void AudioOutputDeviceTest::StartAudioDevice() { 162 audio_device_->Start(); 163 164 EXPECT_CALL(*audio_output_ipc_, CreateStream(audio_device_.get(), _, 0)); 165 166 io_loop_.RunUntilIdle(); 167} 168 169void AudioOutputDeviceTest::CreateStream() { 170 const int kMemorySize = CalculateMemorySize(); 171 172 ASSERT_TRUE(shared_memory_.CreateAndMapAnonymous(kMemorySize)); 173 memset(shared_memory_.memory(), 0xff, kMemorySize); 174 175 ASSERT_TRUE(CancelableSyncSocket::CreatePair(&browser_socket_, 176 &renderer_socket_)); 177 178 // Create duplicates of the handles we pass to AudioOutputDevice since 179 // ownership will be transferred and AudioOutputDevice is responsible for 180 // freeing. 181 SyncSocket::Handle audio_device_socket = SyncSocket::kInvalidHandle; 182 ASSERT_TRUE(DuplicateSocketHandle(renderer_socket_.handle(), 183 &audio_device_socket)); 184 base::SharedMemoryHandle duplicated_memory_handle; 185 ASSERT_TRUE(shared_memory_.ShareToProcess(base::GetCurrentProcessHandle(), 186 &duplicated_memory_handle)); 187 188 audio_device_->OnStreamCreated(duplicated_memory_handle, audio_device_socket, 189 kMemorySize); 190 io_loop_.RunUntilIdle(); 191} 192 193void AudioOutputDeviceTest::ExpectRenderCallback() { 194 // We should get a 'play' notification when we call OnStreamCreated(). 195 // Respond by asking for some audio data. This should ask our callback 196 // to provide some audio data that AudioOutputDevice then writes into the 197 // shared memory section. 198 const int kMemorySize = CalculateMemorySize(); 199 200 EXPECT_CALL(*audio_output_ipc_, PlayStream()) 201 .WillOnce(SendPendingBytes(&browser_socket_, kMemorySize)); 202 203 // We expect calls to our audio renderer callback, which returns the number 204 // of frames written to the memory section. 205 // Here's the second place where it gets hacky: There's no way for us to 206 // know (without using a sleep loop!) when the AudioOutputDevice has finished 207 // writing the interleaved audio data into the shared memory section. 208 // So, for the sake of this test, we consider the call to Render a sign 209 // of success and quit the loop. 210 if (synchronized_io_) { 211 // For synchronized I/O, we expect RenderIO(). 212 EXPECT_CALL(callback_, RenderIO(_, _, _)) 213 .WillOnce(QuitLoop(io_loop_.message_loop_proxy())); 214 } else { 215 // For output only we expect Render(). 216 const int kNumberOfFramesToProcess = 0; 217 EXPECT_CALL(callback_, Render(_, _)) 218 .WillOnce(DoAll( 219 QuitLoop(io_loop_.message_loop_proxy()), 220 Return(kNumberOfFramesToProcess))); 221 } 222} 223 224void AudioOutputDeviceTest::WaitUntilRenderCallback() { 225 // Don't hang the test if we never get the Render() callback. 226 io_loop_.PostDelayedTask(FROM_HERE, base::MessageLoop::QuitClosure(), 227 TestTimeouts::action_timeout()); 228 io_loop_.Run(); 229} 230 231void AudioOutputDeviceTest::StopAudioDevice() { 232 audio_device_->Stop(); 233 234 EXPECT_CALL(*audio_output_ipc_, CloseStream()); 235 236 io_loop_.RunUntilIdle(); 237} 238 239TEST_P(AudioOutputDeviceTest, Initialize) { 240 // Tests that the object can be constructed, initialized and destructed 241 // without having ever been started/stopped. 242} 243 244// Calls Start() followed by an immediate Stop() and check for the basic message 245// filter messages being sent in that case. 246TEST_P(AudioOutputDeviceTest, StartStop) { 247 StartAudioDevice(); 248 StopAudioDevice(); 249} 250 251// AudioOutputDevice supports multiple start/stop sequences. 252TEST_P(AudioOutputDeviceTest, StartStopStartStop) { 253 StartAudioDevice(); 254 StopAudioDevice(); 255 StartAudioDevice(); 256 StopAudioDevice(); 257} 258 259// Simulate receiving OnStreamCreated() prior to processing ShutDownOnIOThread() 260// on the IO loop. 261TEST_P(AudioOutputDeviceTest, StopBeforeRender) { 262 StartAudioDevice(); 263 264 // Call Stop() but don't run the IO loop yet. 265 audio_device_->Stop(); 266 267 // Expect us to shutdown IPC but not to render anything despite the stream 268 // getting created. 269 EXPECT_CALL(*audio_output_ipc_, CloseStream()); 270 CreateStream(); 271} 272 273// Full test with output only. 274TEST_P(AudioOutputDeviceTest, CreateStream) { 275 StartAudioDevice(); 276 ExpectRenderCallback(); 277 CreateStream(); 278 WaitUntilRenderCallback(); 279 StopAudioDevice(); 280} 281 282INSTANTIATE_TEST_CASE_P(Render, AudioOutputDeviceTest, Values(false)); 283INSTANTIATE_TEST_CASE_P(RenderIO, AudioOutputDeviceTest, Values(true)); 284 285} // namespace media. 286