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