Add IPC interface for switching the audio output device for a given audio stream...
[chromium-blink-merge.git] / content / browser / renderer_host / media / audio_renderer_host_unittest.cc
blob08fa8aa0960624d8a36891d89fe353899120e021
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.
5 #include "base/bind.h"
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"
25 using ::testing::_;
26 using ::testing::Assign;
27 using ::testing::DoAll;
28 using ::testing::NotNull;
30 namespace {
31 const int kRenderProcessId = 1;
32 const int kRenderFrameId = 5;
33 const int kStreamId = 50;
34 const int kBadStreamId = 99;
35 const int kSwitchOutputDeviceRequestId = 1;
36 const GURL kSecurityOrigin("http://localhost");
37 const std::string kDefaultDeviceID = "";
38 const std::string kBadDeviceID = "bad-device-id";
39 } // namespace
41 namespace content {
43 class MockAudioMirroringManager : public AudioMirroringManager {
44 public:
45 MockAudioMirroringManager() {}
46 virtual ~MockAudioMirroringManager() {}
48 MOCK_METHOD3(AddDiverter,
49 void(int render_process_id,
50 int render_frame_id,
51 Diverter* diverter));
52 MOCK_METHOD1(RemoveDiverter, void(Diverter* diverter));
54 private:
55 DISALLOW_COPY_AND_ASSIGN(MockAudioMirroringManager);
58 class MockAudioRendererHost : public AudioRendererHost {
59 public:
60 MockAudioRendererHost(media::AudioManager* audio_manager,
61 AudioMirroringManager* mirroring_manager,
62 MediaInternals* media_internals,
63 MediaStreamManager* media_stream_manager,
64 const ResourceContext::SaltCallback& salt_callback)
65 : AudioRendererHost(kRenderProcessId,
66 audio_manager,
67 mirroring_manager,
68 media_internals,
69 media_stream_manager,
70 salt_callback),
71 shared_memory_length_(0) {}
73 // A list of mock methods.
74 MOCK_METHOD2(OnStreamCreated, void(int stream_id, int length));
75 MOCK_METHOD1(OnStreamPlaying, void(int stream_id));
76 MOCK_METHOD1(OnStreamPaused, void(int stream_id));
77 MOCK_METHOD1(OnStreamError, void(int stream_id));
78 MOCK_METHOD3(OnOutputDeviceSwitched,
79 void(int stream_id,
80 int request_id,
81 media::SwitchOutputDeviceResult result));
83 private:
84 virtual ~MockAudioRendererHost() {
85 // Make sure all audio streams have been deleted.
86 EXPECT_TRUE(audio_entries_.empty());
89 // This method is used to dispatch IPC messages to the renderer. We intercept
90 // these messages here and dispatch to our mock methods to verify the
91 // conversation between this object and the renderer.
92 virtual bool Send(IPC::Message* message) {
93 CHECK(message);
95 // In this method we dispatch the messages to the according handlers as if
96 // we are the renderer.
97 bool handled = true;
98 IPC_BEGIN_MESSAGE_MAP(MockAudioRendererHost, *message)
99 IPC_MESSAGE_HANDLER(AudioMsg_NotifyStreamCreated,
100 OnNotifyStreamCreated)
101 IPC_MESSAGE_HANDLER(AudioMsg_NotifyStreamStateChanged,
102 OnNotifyStreamStateChanged)
103 IPC_MESSAGE_HANDLER(AudioMsg_NotifyOutputDeviceSwitched,
104 OnNotifyOutputDeviceSwitched)
105 IPC_MESSAGE_UNHANDLED(handled = false)
106 IPC_END_MESSAGE_MAP()
107 EXPECT_TRUE(handled);
109 delete message;
110 return true;
113 void OnNotifyStreamCreated(
114 int stream_id, base::SharedMemoryHandle handle,
115 base::SyncSocket::TransitDescriptor socket_descriptor, uint32 length) {
116 // Maps the shared memory.
117 shared_memory_.reset(new base::SharedMemory(handle, false));
118 CHECK(shared_memory_->Map(length));
119 CHECK(shared_memory_->memory());
120 shared_memory_length_ = length;
122 // Create the SyncSocket using the handle.
123 base::SyncSocket::Handle sync_socket_handle =
124 base::SyncSocket::UnwrapHandle(socket_descriptor);
125 sync_socket_.reset(new base::SyncSocket(sync_socket_handle));
127 // And then delegate the call to the mock method.
128 OnStreamCreated(stream_id, length);
131 void OnNotifyStreamStateChanged(int stream_id,
132 media::AudioOutputIPCDelegate::State state) {
133 switch (state) {
134 case media::AudioOutputIPCDelegate::kPlaying:
135 OnStreamPlaying(stream_id);
136 break;
137 case media::AudioOutputIPCDelegate::kPaused:
138 OnStreamPaused(stream_id);
139 break;
140 case media::AudioOutputIPCDelegate::kError:
141 OnStreamError(stream_id);
142 break;
143 default:
144 FAIL() << "Unknown stream state";
145 break;
149 void OnNotifyOutputDeviceSwitched(int stream_id,
150 int request_id,
151 media::SwitchOutputDeviceResult result) {
152 switch (result) {
153 case media::SWITCH_OUTPUT_DEVICE_RESULT_SUCCESS:
154 case media::SWITCH_OUTPUT_DEVICE_RESULT_ERROR_NOT_FOUND:
155 case media::SWITCH_OUTPUT_DEVICE_RESULT_ERROR_NOT_AUTHORIZED:
156 case media::SWITCH_OUTPUT_DEVICE_RESULT_ERROR_OBSOLETE:
157 case media::SWITCH_OUTPUT_DEVICE_RESULT_ERROR_NOT_SUPPORTED:
158 OnOutputDeviceSwitched(stream_id, request_id, result);
159 break;
160 default:
161 FAIL() << "Unknown SwitchOutputDevice result";
162 break;
166 scoped_ptr<base::SharedMemory> shared_memory_;
167 scoped_ptr<base::SyncSocket> sync_socket_;
168 uint32 shared_memory_length_;
170 DISALLOW_COPY_AND_ASSIGN(MockAudioRendererHost);
173 namespace {
174 std::string ReturnMockSalt() {
175 return std::string();
178 ResourceContext::SaltCallback GetMockSaltCallback() {
179 return base::Bind(&ReturnMockSalt);
183 class AudioRendererHostTest : public testing::Test {
184 public:
185 AudioRendererHostTest() {
186 audio_manager_.reset(media::AudioManager::CreateForTesting());
187 base::CommandLine::ForCurrentProcess()->AppendSwitch(
188 switches::kUseFakeDeviceForMediaStream);
189 media_stream_manager_.reset(new MediaStreamManager(audio_manager_.get()));
190 host_ = new MockAudioRendererHost(audio_manager_.get(), &mirroring_manager_,
191 MediaInternals::GetInstance(),
192 media_stream_manager_.get(),
193 GetMockSaltCallback());
195 // Simulate IPC channel connected.
196 host_->set_peer_process_for_testing(base::Process::Current());
199 ~AudioRendererHostTest() override {
200 // Simulate closing the IPC channel and give the audio thread time to close
201 // the underlying streams.
202 host_->OnChannelClosing();
203 SyncWithAudioThread();
205 // Release the reference to the mock object. The object will be destructed
206 // on message_loop_.
207 host_ = NULL;
210 protected:
211 void Create(bool unified_stream) {
212 EXPECT_CALL(*host_.get(), OnStreamCreated(kStreamId, _));
214 EXPECT_CALL(mirroring_manager_,
215 AddDiverter(kRenderProcessId, kRenderFrameId, NotNull()))
216 .RetiresOnSaturation();
218 // Send a create stream message to the audio output stream and wait until
219 // we receive the created message.
220 int session_id;
221 media::AudioParameters params;
222 if (unified_stream) {
223 // Use AudioInputDeviceManager::kFakeOpenSessionId as the session id to
224 // pass the permission check.
225 session_id = AudioInputDeviceManager::kFakeOpenSessionId;
226 params = media::AudioParameters(
227 media::AudioParameters::AUDIO_FAKE,
228 media::CHANNEL_LAYOUT_STEREO,
229 media::AudioParameters::kAudioCDSampleRate, 16,
230 media::AudioParameters::kAudioCDSampleRate / 10,
231 media::AudioParameters::NO_EFFECTS);
232 } else {
233 session_id = 0;
234 params = media::AudioParameters(
235 media::AudioParameters::AUDIO_FAKE,
236 media::CHANNEL_LAYOUT_STEREO,
237 media::AudioParameters::kAudioCDSampleRate, 16,
238 media::AudioParameters::kAudioCDSampleRate / 10);
240 host_->OnCreateStream(kStreamId, kRenderFrameId, session_id, params);
242 // At some point in the future, a corresponding RemoveDiverter() call must
243 // be made.
244 EXPECT_CALL(mirroring_manager_, RemoveDiverter(NotNull()))
245 .RetiresOnSaturation();
246 SyncWithAudioThread();
249 void Close() {
250 // Send a message to AudioRendererHost to tell it we want to close the
251 // stream.
252 host_->OnCloseStream(kStreamId);
253 SyncWithAudioThread();
256 void Play() {
257 EXPECT_CALL(*host_.get(), OnStreamPlaying(kStreamId));
258 host_->OnPlayStream(kStreamId);
259 SyncWithAudioThread();
262 void Pause() {
263 EXPECT_CALL(*host_.get(), OnStreamPaused(kStreamId));
264 host_->OnPauseStream(kStreamId);
265 SyncWithAudioThread();
268 void SetVolume(double volume) {
269 host_->OnSetVolume(kStreamId, volume);
270 SyncWithAudioThread();
273 void SwitchOutputDevice(int stream_id,
274 std::string device_id,
275 media::SwitchOutputDeviceResult expected_result) {
276 EXPECT_CALL(*host_.get(),
277 OnOutputDeviceSwitched(stream_id, kSwitchOutputDeviceRequestId,
278 expected_result));
279 host_->OnSwitchOutputDevice(stream_id, kRenderFrameId, device_id,
280 kSecurityOrigin, kSwitchOutputDeviceRequestId);
281 SyncWithAudioThread();
284 void SimulateError() {
285 EXPECT_EQ(1u, host_->audio_entries_.size())
286 << "Calls Create() before calling this method";
288 // Expect an error signal sent through IPC.
289 EXPECT_CALL(*host_.get(), OnStreamError(kStreamId));
291 // Simulate an error sent from the audio device.
292 host_->ReportErrorAndClose(kStreamId);
293 SyncWithAudioThread();
295 // Expect the audio stream record is removed.
296 EXPECT_EQ(0u, host_->audio_entries_.size());
299 // SyncWithAudioThread() waits until all pending tasks on the audio thread
300 // are executed while also processing pending task in message_loop_ on the
301 // current thread. It is used to synchronize with the audio thread when we are
302 // closing an audio stream.
303 void SyncWithAudioThread() {
304 base::RunLoop().RunUntilIdle();
306 base::RunLoop run_loop;
307 audio_manager_->GetTaskRunner()->PostTask(
308 FROM_HERE, media::BindToCurrentLoop(run_loop.QuitClosure()));
309 run_loop.Run();
312 private:
313 // MediaStreamManager uses a DestructionObserver, so it must outlive the
314 // TestBrowserThreadBundle.
315 scoped_ptr<MediaStreamManager> media_stream_manager_;
316 TestBrowserThreadBundle thread_bundle_;
317 scoped_ptr<media::AudioManager> audio_manager_;
318 MockAudioMirroringManager mirroring_manager_;
319 scoped_refptr<MockAudioRendererHost> host_;
321 DISALLOW_COPY_AND_ASSIGN(AudioRendererHostTest);
324 TEST_F(AudioRendererHostTest, CreateAndClose) {
325 Create(false);
326 Close();
329 // Simulate the case where a stream is not properly closed.
330 TEST_F(AudioRendererHostTest, CreateAndShutdown) {
331 Create(false);
334 TEST_F(AudioRendererHostTest, CreatePlayAndClose) {
335 Create(false);
336 Play();
337 Close();
340 TEST_F(AudioRendererHostTest, CreatePlayPauseAndClose) {
341 Create(false);
342 Play();
343 Pause();
344 Close();
347 TEST_F(AudioRendererHostTest, SetVolume) {
348 Create(false);
349 SetVolume(0.5);
350 Play();
351 Pause();
352 Close();
355 TEST_F(AudioRendererHostTest, SwitchOutputDevice) {
356 Create(false);
357 SwitchOutputDevice(kStreamId, kDefaultDeviceID,
358 media::SWITCH_OUTPUT_DEVICE_RESULT_SUCCESS);
359 Close();
362 TEST_F(AudioRendererHostTest, SwitchOutputDeviceNotAuthorized) {
363 Create(false);
364 SwitchOutputDevice(kStreamId, kBadDeviceID,
365 media::SWITCH_OUTPUT_DEVICE_RESULT_ERROR_NOT_AUTHORIZED);
366 Close();
369 TEST_F(AudioRendererHostTest, SwitchOutputDeviceNoStream) {
370 Create(false);
371 SwitchOutputDevice(kBadStreamId, kDefaultDeviceID,
372 media::SWITCH_OUTPUT_DEVICE_RESULT_ERROR_OBSOLETE);
373 Close();
376 // Simulate the case where a stream is not properly closed.
377 TEST_F(AudioRendererHostTest, CreatePlayAndShutdown) {
378 Create(false);
379 Play();
382 // Simulate the case where a stream is not properly closed.
383 TEST_F(AudioRendererHostTest, CreatePlayPauseAndShutdown) {
384 Create(false);
385 Play();
386 Pause();
389 TEST_F(AudioRendererHostTest, SimulateError) {
390 Create(false);
391 Play();
392 SimulateError();
395 // Simulate the case when an error is generated on the browser process,
396 // the audio device is closed but the render process try to close the
397 // audio stream again.
398 TEST_F(AudioRendererHostTest, SimulateErrorAndClose) {
399 Create(false);
400 Play();
401 SimulateError();
402 Close();
405 TEST_F(AudioRendererHostTest, CreateUnifiedStreamAndClose) {
406 Create(true);
407 Close();
410 // TODO(hclam): Add tests for data conversation in low latency mode.
412 } // namespace content