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