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.
6 #include "base/command_line.h"
7 #include "base/memory/scoped_ptr.h"
8 #include "base/run_loop.h"
9 #include "base/sync_socket.h"
10 #include "content/browser/media/capture/audio_mirroring_manager.h"
11 #include "content/browser/media/media_internals.h"
12 #include "content/browser/renderer_host/media/audio_input_device_manager.h"
13 #include "content/browser/renderer_host/media/audio_renderer_host.h"
14 #include "content/browser/renderer_host/media/media_stream_manager.h"
15 #include "content/common/media/audio_messages.h"
16 #include "content/public/common/content_switches.h"
17 #include "content/public/test/test_browser_thread_bundle.h"
18 #include "ipc/ipc_message_utils.h"
19 #include "media/audio/audio_manager.h"
20 #include "media/base/bind_to_current_loop.h"
21 #include "media/base/media_switches.h"
22 #include "testing/gmock/include/gmock/gmock.h"
23 #include "testing/gtest/include/gtest/gtest.h"
26 using ::testing::Assign
;
27 using ::testing::DoAll
;
28 using ::testing::NotNull
;
31 const int kRenderProcessId
= 1;
32 const int kRenderFrameId
= 5;
33 const int kStreamId
= 50;
34 const int kBadStreamId
= 99;
35 const url::Origin
kSecurityOrigin(GURL("http://localhost"));
36 const url::Origin kDefaultSecurityOrigin
;
37 const std::string kDefaultDeviceID
;
38 const std::string kBadDeviceID
=
39 "badbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbad1";
40 const std::string kInvalidDeviceID
= "invalid-device-id";
45 class MockAudioMirroringManager
: public AudioMirroringManager
{
47 MockAudioMirroringManager() {}
48 virtual ~MockAudioMirroringManager() {}
50 MOCK_METHOD3(AddDiverter
,
51 void(int render_process_id
,
54 MOCK_METHOD1(RemoveDiverter
, void(Diverter
* diverter
));
57 DISALLOW_COPY_AND_ASSIGN(MockAudioMirroringManager
);
60 class MockAudioRendererHost
: public AudioRendererHost
{
62 MockAudioRendererHost(media::AudioManager
* audio_manager
,
63 AudioMirroringManager
* mirroring_manager
,
64 MediaInternals
* media_internals
,
65 MediaStreamManager
* media_stream_manager
,
66 const ResourceContext::SaltCallback
& salt_callback
)
67 : AudioRendererHost(kRenderProcessId
,
73 shared_memory_length_(0) {}
75 // A list of mock methods.
76 MOCK_METHOD3(OnDeviceAuthorized
,
79 const media::AudioParameters
& output_params
));
80 MOCK_METHOD2(OnStreamCreated
, void(int stream_id
, int length
));
81 MOCK_METHOD1(OnStreamPlaying
, void(int stream_id
));
82 MOCK_METHOD1(OnStreamPaused
, void(int stream_id
));
83 MOCK_METHOD1(OnStreamError
, void(int stream_id
));
84 MOCK_METHOD2(OnOutputDeviceSwitched
,
85 void(int stream_id
, media::SwitchOutputDeviceResult result
));
88 virtual ~MockAudioRendererHost() {
89 // Make sure all audio streams have been deleted.
90 EXPECT_TRUE(audio_entries_
.empty());
93 // This method is used to dispatch IPC messages to the renderer. We intercept
94 // these messages here and dispatch to our mock methods to verify the
95 // conversation between this object and the renderer.
96 virtual bool Send(IPC::Message
* message
) {
99 // In this method we dispatch the messages to the according handlers as if
100 // we are the renderer.
102 IPC_BEGIN_MESSAGE_MAP(MockAudioRendererHost
, *message
)
103 IPC_MESSAGE_HANDLER(AudioMsg_NotifyDeviceAuthorized
,
104 OnNotifyDeviceAuthorized
)
105 IPC_MESSAGE_HANDLER(AudioMsg_NotifyStreamCreated
,
106 OnNotifyStreamCreated
)
107 IPC_MESSAGE_HANDLER(AudioMsg_NotifyStreamStateChanged
,
108 OnNotifyStreamStateChanged
)
109 IPC_MESSAGE_HANDLER(AudioMsg_NotifyOutputDeviceSwitched
,
110 OnNotifyOutputDeviceSwitched
)
111 IPC_MESSAGE_UNHANDLED(handled
= false)
112 IPC_END_MESSAGE_MAP()
113 EXPECT_TRUE(handled
);
119 void OnNotifyDeviceAuthorized(int stream_id
,
121 const media::AudioParameters
& output_params
) {
122 OnDeviceAuthorized(stream_id
, success
, output_params
);
125 void OnNotifyStreamCreated(
126 int stream_id
, base::SharedMemoryHandle handle
,
127 base::SyncSocket::TransitDescriptor socket_descriptor
, uint32 length
) {
128 // Maps the shared memory.
129 shared_memory_
.reset(new base::SharedMemory(handle
, false));
130 CHECK(shared_memory_
->Map(length
));
131 CHECK(shared_memory_
->memory());
132 shared_memory_length_
= length
;
134 // Create the SyncSocket using the handle.
135 base::SyncSocket::Handle sync_socket_handle
=
136 base::SyncSocket::UnwrapHandle(socket_descriptor
);
137 sync_socket_
.reset(new base::SyncSocket(sync_socket_handle
));
139 // And then delegate the call to the mock method.
140 OnStreamCreated(stream_id
, length
);
143 void OnNotifyStreamStateChanged(int stream_id
,
144 media::AudioOutputIPCDelegateState state
) {
146 case media::AUDIO_OUTPUT_IPC_DELEGATE_STATE_PLAYING
:
147 OnStreamPlaying(stream_id
);
149 case media::AUDIO_OUTPUT_IPC_DELEGATE_STATE_PAUSED
:
150 OnStreamPaused(stream_id
);
152 case media::AUDIO_OUTPUT_IPC_DELEGATE_STATE_ERROR
:
153 OnStreamError(stream_id
);
156 FAIL() << "Unknown stream state";
161 void OnNotifyOutputDeviceSwitched(int stream_id
,
162 media::SwitchOutputDeviceResult result
) {
164 case media::SWITCH_OUTPUT_DEVICE_RESULT_SUCCESS
:
165 case media::SWITCH_OUTPUT_DEVICE_RESULT_ERROR_NOT_FOUND
:
166 case media::SWITCH_OUTPUT_DEVICE_RESULT_ERROR_NOT_AUTHORIZED
:
167 case media::SWITCH_OUTPUT_DEVICE_RESULT_ERROR_INTERNAL
:
168 OnOutputDeviceSwitched(stream_id
, result
);
171 FAIL() << "Unknown SwitchOutputDevice result";
176 scoped_ptr
<base::SharedMemory
> shared_memory_
;
177 scoped_ptr
<base::SyncSocket
> sync_socket_
;
178 uint32 shared_memory_length_
;
180 DISALLOW_COPY_AND_ASSIGN(MockAudioRendererHost
);
184 std::string
ReturnMockSalt() {
185 return std::string();
188 ResourceContext::SaltCallback
GetMockSaltCallback() {
189 return base::Bind(&ReturnMockSalt
);
192 void WaitForEnumeration(base::RunLoop
* loop
,
193 const AudioOutputDeviceEnumeration
& e
) {
198 class AudioRendererHostTest
: public testing::Test
{
200 AudioRendererHostTest() {
201 audio_manager_
.reset(media::AudioManager::CreateForTesting());
202 base::CommandLine::ForCurrentProcess()->AppendSwitch(
203 switches::kUseFakeDeviceForMediaStream
);
204 media_stream_manager_
.reset(new MediaStreamManager(audio_manager_
.get()));
206 // Enable caching to make enumerations run in a single thread
207 media_stream_manager_
->audio_output_device_enumerator()->SetCachePolicy(
208 AudioOutputDeviceEnumerator::CACHE_POLICY_MANUAL_INVALIDATION
);
209 base::RunLoop().RunUntilIdle();
210 base::RunLoop run_loop
;
211 media_stream_manager_
->audio_output_device_enumerator()->Enumerate(
212 base::Bind(&WaitForEnumeration
, &run_loop
));
215 host_
= new MockAudioRendererHost(audio_manager_
.get(), &mirroring_manager_
,
216 MediaInternals::GetInstance(),
217 media_stream_manager_
.get(),
218 GetMockSaltCallback());
220 // Simulate IPC channel connected.
221 host_
->set_peer_process_for_testing(base::Process::Current());
224 ~AudioRendererHostTest() override
{
225 // Simulate closing the IPC channel and give the audio thread time to close
226 // the underlying streams.
227 host_
->OnChannelClosing();
228 SyncWithAudioThread();
230 // Release the reference to the mock object. The object will be destructed
236 void Create(bool unified_stream
) {
237 EXPECT_CALL(*host_
.get(), OnDeviceAuthorized(kStreamId
, true, _
));
238 EXPECT_CALL(*host_
.get(), OnStreamCreated(kStreamId
, _
));
240 EXPECT_CALL(mirroring_manager_
,
241 AddDiverter(kRenderProcessId
, kRenderFrameId
, NotNull()))
242 .RetiresOnSaturation();
244 // Send a create stream message to the audio output stream and wait until
245 // we receive the created message.
246 media::AudioParameters
params(
247 media::AudioParameters::AUDIO_FAKE
, media::CHANNEL_LAYOUT_STEREO
,
248 media::AudioParameters::kAudioCDSampleRate
, 16,
249 media::AudioParameters::kAudioCDSampleRate
/ 10);
251 if (unified_stream
) {
252 // Use AudioInputDeviceManager::kFakeOpenSessionId as the session id to
253 // pass the permission check.
254 session_id
= AudioInputDeviceManager::kFakeOpenSessionId
;
256 host_
->OnRequestDeviceAuthorization(kStreamId
, kRenderFrameId
, session_id
,
258 kDefaultSecurityOrigin
);
259 host_
->OnCreateStream(kStreamId
, kRenderFrameId
, params
);
261 // At some point in the future, a corresponding RemoveDiverter() call must
263 EXPECT_CALL(mirroring_manager_
, RemoveDiverter(NotNull()))
264 .RetiresOnSaturation();
265 SyncWithAudioThread();
269 // Send a message to AudioRendererHost to tell it we want to close the
271 host_
->OnCloseStream(kStreamId
);
272 SyncWithAudioThread();
276 EXPECT_CALL(*host_
.get(), OnStreamPlaying(kStreamId
));
277 host_
->OnPlayStream(kStreamId
);
278 SyncWithAudioThread();
282 EXPECT_CALL(*host_
.get(), OnStreamPaused(kStreamId
));
283 host_
->OnPauseStream(kStreamId
);
284 SyncWithAudioThread();
287 void SetVolume(double volume
) {
288 host_
->OnSetVolume(kStreamId
, volume
);
289 SyncWithAudioThread();
292 void SwitchOutputDevice(int stream_id
,
293 const std::string
& device_id
,
294 const url::Origin
& security_origin
,
295 media::SwitchOutputDeviceResult expected_result
) {
296 EXPECT_CALL(*host_
.get(),
297 OnOutputDeviceSwitched(stream_id
, expected_result
));
298 host_
->OnSwitchOutputDevice(stream_id
, kRenderFrameId
, device_id
,
300 SyncWithAudioThread();
303 void SimulateError() {
304 EXPECT_EQ(1u, host_
->audio_entries_
.size())
305 << "Calls Create() before calling this method";
307 // Expect an error signal sent through IPC.
308 EXPECT_CALL(*host_
.get(), OnStreamError(kStreamId
));
310 // Simulate an error sent from the audio device.
311 host_
->ReportErrorAndClose(kStreamId
);
312 SyncWithAudioThread();
314 // Expect the audio stream record is removed.
315 EXPECT_EQ(0u, host_
->audio_entries_
.size());
318 // SyncWithAudioThread() waits until all pending tasks on the audio thread
319 // are executed while also processing pending task in message_loop_ on the
320 // current thread. It is used to synchronize with the audio thread when we are
321 // closing an audio stream.
322 void SyncWithAudioThread() {
323 base::RunLoop().RunUntilIdle();
325 base::RunLoop run_loop
;
326 audio_manager_
->GetTaskRunner()->PostTask(
327 FROM_HERE
, media::BindToCurrentLoop(run_loop
.QuitClosure()));
332 // MediaStreamManager uses a DestructionObserver, so it must outlive the
333 // TestBrowserThreadBundle.
334 scoped_ptr
<MediaStreamManager
> media_stream_manager_
;
335 TestBrowserThreadBundle thread_bundle_
;
336 scoped_ptr
<media::AudioManager
> audio_manager_
;
337 MockAudioMirroringManager mirroring_manager_
;
338 scoped_refptr
<MockAudioRendererHost
> host_
;
340 DISALLOW_COPY_AND_ASSIGN(AudioRendererHostTest
);
343 TEST_F(AudioRendererHostTest
, CreateAndClose
) {
348 // Simulate the case where a stream is not properly closed.
349 TEST_F(AudioRendererHostTest
, CreateAndShutdown
) {
353 TEST_F(AudioRendererHostTest
, CreatePlayAndClose
) {
359 TEST_F(AudioRendererHostTest
, CreatePlayPauseAndClose
) {
366 TEST_F(AudioRendererHostTest
, SetVolume
) {
374 TEST_F(AudioRendererHostTest
, SwitchOutputDevice
) {
376 SwitchOutputDevice(kStreamId
, kDefaultDeviceID
, kDefaultSecurityOrigin
,
377 media::SWITCH_OUTPUT_DEVICE_RESULT_SUCCESS
);
381 TEST_F(AudioRendererHostTest
, SwitchOutputDeviceNotAuthorized
) {
383 SwitchOutputDevice(kStreamId
, kBadDeviceID
, kSecurityOrigin
,
384 media::SWITCH_OUTPUT_DEVICE_RESULT_ERROR_NOT_AUTHORIZED
);
388 TEST_F(AudioRendererHostTest
, SwitchOutputDeviceInvalidDeviceId
) {
390 SwitchOutputDevice(kStreamId
, kInvalidDeviceID
, kSecurityOrigin
,
391 media::SWITCH_OUTPUT_DEVICE_RESULT_ERROR_INTERNAL
);
395 TEST_F(AudioRendererHostTest
, SwitchOutputDeviceNoStream
) {
397 SwitchOutputDevice(kBadStreamId
, kDefaultDeviceID
, kDefaultSecurityOrigin
,
398 media::SWITCH_OUTPUT_DEVICE_RESULT_ERROR_INTERNAL
);
402 // Simulate the case where a stream is not properly closed.
403 TEST_F(AudioRendererHostTest
, CreatePlayAndShutdown
) {
408 // Simulate the case where a stream is not properly closed.
409 TEST_F(AudioRendererHostTest
, CreatePlayPauseAndShutdown
) {
415 TEST_F(AudioRendererHostTest
, SimulateError
) {
421 // Simulate the case when an error is generated on the browser process,
422 // the audio device is closed but the render process try to close the
423 // audio stream again.
424 TEST_F(AudioRendererHostTest
, SimulateErrorAndClose
) {
431 TEST_F(AudioRendererHostTest
, CreateUnifiedStreamAndClose
) {
436 // TODO(hclam): Add tests for data conversation in low latency mode.
438 } // namespace content