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