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