audio_renderer_host_unittest.cc revision 5c02ac1a9c1b504631c0a3d2b6e737b5d738bae1
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 "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 "testing/gmock/include/gmock/gmock.h"
22#include "testing/gtest/include/gtest/gtest.h"
23
24using ::testing::_;
25using ::testing::Assign;
26using ::testing::DoAll;
27using ::testing::NotNull;
28
29namespace {
30const int kRenderProcessId = 1;
31const int kRenderViewId = 4;
32const int kRenderFrameId = 5;
33const int kStreamId = 50;
34}  // namespace
35
36namespace content {
37
38class MockAudioMirroringManager : public AudioMirroringManager {
39 public:
40  MockAudioMirroringManager() {}
41  virtual ~MockAudioMirroringManager() {}
42
43  MOCK_METHOD3(AddDiverter,
44               void(int render_process_id,
45                    int render_view_id,
46                    Diverter* diverter));
47  MOCK_METHOD3(RemoveDiverter,
48               void(int render_process_id,
49                    int render_view_id,
50                    Diverter* diverter));
51
52 private:
53  DISALLOW_COPY_AND_ASSIGN(MockAudioMirroringManager);
54};
55
56class MockAudioRendererHost : public AudioRendererHost {
57 public:
58  MockAudioRendererHost(media::AudioManager* audio_manager,
59                        AudioMirroringManager* mirroring_manager,
60                        MediaInternals* media_internals,
61                        MediaStreamManager* media_stream_manager)
62      : AudioRendererHost(kRenderProcessId,
63                          audio_manager,
64                          mirroring_manager,
65                          media_internals,
66                          media_stream_manager),
67        shared_memory_length_(0) {}
68
69  // A list of mock methods.
70  MOCK_METHOD2(OnStreamCreated, void(int stream_id, int length));
71  MOCK_METHOD1(OnStreamPlaying, void(int stream_id));
72  MOCK_METHOD1(OnStreamPaused, void(int stream_id));
73  MOCK_METHOD1(OnStreamError, void(int stream_id));
74
75 private:
76  virtual ~MockAudioRendererHost() {
77    // Make sure all audio streams have been deleted.
78    EXPECT_TRUE(audio_entries_.empty());
79  }
80
81  // This method is used to dispatch IPC messages to the renderer. We intercept
82  // these messages here and dispatch to our mock methods to verify the
83  // conversation between this object and the renderer.
84  virtual bool Send(IPC::Message* message) {
85    CHECK(message);
86
87    // In this method we dispatch the messages to the according handlers as if
88    // we are the renderer.
89    bool handled = true;
90    IPC_BEGIN_MESSAGE_MAP(MockAudioRendererHost, *message)
91      IPC_MESSAGE_HANDLER(AudioMsg_NotifyStreamCreated,
92                          OnStreamCreated)
93      IPC_MESSAGE_HANDLER(AudioMsg_NotifyStreamStateChanged,
94                          OnStreamStateChanged)
95      IPC_MESSAGE_UNHANDLED(handled = false)
96    IPC_END_MESSAGE_MAP()
97    EXPECT_TRUE(handled);
98
99    delete message;
100    return true;
101  }
102
103  void OnStreamCreated(const IPC::Message& msg,
104                       int stream_id,
105                       base::SharedMemoryHandle handle,
106#if defined(OS_WIN)
107                       base::SyncSocket::Handle socket_handle,
108#else
109                       base::FileDescriptor socket_descriptor,
110#endif
111                       uint32 length) {
112    // Maps the shared memory.
113    shared_memory_.reset(new base::SharedMemory(handle, false));
114    CHECK(shared_memory_->Map(length));
115    CHECK(shared_memory_->memory());
116    shared_memory_length_ = length;
117
118    // Create the SyncSocket using the handle.
119    base::SyncSocket::Handle sync_socket_handle;
120#if defined(OS_WIN)
121    sync_socket_handle = socket_handle;
122#else
123    sync_socket_handle = socket_descriptor.fd;
124#endif
125    sync_socket_.reset(new base::SyncSocket(sync_socket_handle));
126
127    // And then delegate the call to the mock method.
128    OnStreamCreated(stream_id, length);
129  }
130
131  void OnStreamStateChanged(const IPC::Message& msg,
132                            int stream_id,
133                            media::AudioOutputIPCDelegate::State state) {
134    switch (state) {
135      case media::AudioOutputIPCDelegate::kPlaying:
136        OnStreamPlaying(stream_id);
137        break;
138      case media::AudioOutputIPCDelegate::kPaused:
139        OnStreamPaused(stream_id);
140        break;
141      case media::AudioOutputIPCDelegate::kError:
142        OnStreamError(stream_id);
143        break;
144      default:
145        FAIL() << "Unknown stream state";
146        break;
147    }
148  }
149
150  scoped_ptr<base::SharedMemory> shared_memory_;
151  scoped_ptr<base::SyncSocket> sync_socket_;
152  uint32 shared_memory_length_;
153
154  DISALLOW_COPY_AND_ASSIGN(MockAudioRendererHost);
155};
156
157class AudioRendererHostTest : public testing::Test {
158 public:
159  AudioRendererHostTest() {
160    audio_manager_.reset(media::AudioManager::CreateForTesting());
161    CommandLine::ForCurrentProcess()->AppendSwitch(
162        switches::kUseFakeDeviceForMediaStream);
163    media_stream_manager_.reset(new MediaStreamManager(audio_manager_.get()));
164    host_ = new MockAudioRendererHost(audio_manager_.get(),
165                                      &mirroring_manager_,
166                                      MediaInternals::GetInstance(),
167                                      media_stream_manager_.get());
168
169    // Simulate IPC channel connected.
170    host_->set_peer_pid_for_testing(base::GetCurrentProcId());
171  }
172
173  virtual ~AudioRendererHostTest() {
174    // Simulate closing the IPC channel and give the audio thread time to close
175    // the underlying streams.
176    host_->OnChannelClosing();
177    SyncWithAudioThread();
178
179    // Release the reference to the mock object.  The object will be destructed
180    // on message_loop_.
181    host_ = NULL;
182  }
183
184 protected:
185  void Create(bool unified_stream) {
186    EXPECT_CALL(*host_.get(), OnStreamCreated(kStreamId, _));
187
188    EXPECT_CALL(mirroring_manager_,
189                AddDiverter(kRenderProcessId, kRenderViewId, NotNull()))
190        .RetiresOnSaturation();
191
192    // Send a create stream message to the audio output stream and wait until
193    // we receive the created message.
194    int session_id;
195    media::AudioParameters params;
196    if (unified_stream) {
197      // Use AudioInputDeviceManager::kFakeOpenSessionId as the session id to
198      // pass the permission check.
199      session_id = AudioInputDeviceManager::kFakeOpenSessionId;
200      params = media::AudioParameters(
201          media::AudioParameters::AUDIO_FAKE,
202          media::CHANNEL_LAYOUT_STEREO,
203          2,
204          media::AudioParameters::kAudioCDSampleRate, 16,
205          media::AudioParameters::kAudioCDSampleRate / 10,
206          media::AudioParameters::NO_EFFECTS);
207    } else {
208      session_id = 0;
209      params = media::AudioParameters(
210          media::AudioParameters::AUDIO_FAKE,
211          media::CHANNEL_LAYOUT_STEREO,
212          media::AudioParameters::kAudioCDSampleRate, 16,
213          media::AudioParameters::kAudioCDSampleRate / 10);
214    }
215    host_->OnCreateStream(kStreamId, kRenderViewId, kRenderFrameId, session_id,
216                          params);
217
218    // At some point in the future, a corresponding RemoveDiverter() call must
219    // be made.
220    EXPECT_CALL(mirroring_manager_,
221                RemoveDiverter(kRenderProcessId, kRenderViewId, NotNull()))
222        .RetiresOnSaturation();
223    SyncWithAudioThread();
224  }
225
226  void Close() {
227    // Send a message to AudioRendererHost to tell it we want to close the
228    // stream.
229    host_->OnCloseStream(kStreamId);
230    SyncWithAudioThread();
231  }
232
233  void Play() {
234    EXPECT_CALL(*host_.get(), OnStreamPlaying(kStreamId));
235    host_->OnPlayStream(kStreamId);
236    SyncWithAudioThread();
237  }
238
239  void Pause() {
240    EXPECT_CALL(*host_.get(), OnStreamPaused(kStreamId));
241    host_->OnPauseStream(kStreamId);
242    SyncWithAudioThread();
243  }
244
245  void SetVolume(double volume) {
246    host_->OnSetVolume(kStreamId, volume);
247    SyncWithAudioThread();
248  }
249
250  void SimulateError() {
251    EXPECT_EQ(1u, host_->audio_entries_.size())
252        << "Calls Create() before calling this method";
253
254    // Expect an error signal sent through IPC.
255    EXPECT_CALL(*host_.get(), OnStreamError(kStreamId));
256
257    // Simulate an error sent from the audio device.
258    host_->ReportErrorAndClose(kStreamId);
259    SyncWithAudioThread();
260
261    // Expect the audio stream record is removed.
262    EXPECT_EQ(0u, host_->audio_entries_.size());
263  }
264
265  // SyncWithAudioThread() waits until all pending tasks on the audio thread
266  // are executed while also processing pending task in message_loop_ on the
267  // current thread. It is used to synchronize with the audio thread when we are
268  // closing an audio stream.
269  void SyncWithAudioThread() {
270    base::RunLoop().RunUntilIdle();
271
272    base::RunLoop run_loop;
273    audio_manager_->GetTaskRunner()->PostTask(
274        FROM_HERE, media::BindToCurrentLoop(run_loop.QuitClosure()));
275    run_loop.Run();
276  }
277
278 private:
279  // MediaStreamManager uses a DestructionObserver, so it must outlive the
280  // TestBrowserThreadBundle.
281  scoped_ptr<MediaStreamManager> media_stream_manager_;
282  TestBrowserThreadBundle thread_bundle_;
283  scoped_ptr<media::AudioManager> audio_manager_;
284  MockAudioMirroringManager mirroring_manager_;
285  scoped_refptr<MockAudioRendererHost> host_;
286
287  DISALLOW_COPY_AND_ASSIGN(AudioRendererHostTest);
288};
289
290TEST_F(AudioRendererHostTest, CreateAndClose) {
291  Create(false);
292  Close();
293}
294
295// Simulate the case where a stream is not properly closed.
296TEST_F(AudioRendererHostTest, CreateAndShutdown) {
297  Create(false);
298}
299
300TEST_F(AudioRendererHostTest, CreatePlayAndClose) {
301  Create(false);
302  Play();
303  Close();
304}
305
306TEST_F(AudioRendererHostTest, CreatePlayPauseAndClose) {
307  Create(false);
308  Play();
309  Pause();
310  Close();
311}
312
313TEST_F(AudioRendererHostTest, SetVolume) {
314  Create(false);
315  SetVolume(0.5);
316  Play();
317  Pause();
318  Close();
319}
320
321// Simulate the case where a stream is not properly closed.
322TEST_F(AudioRendererHostTest, CreatePlayAndShutdown) {
323  Create(false);
324  Play();
325}
326
327// Simulate the case where a stream is not properly closed.
328TEST_F(AudioRendererHostTest, CreatePlayPauseAndShutdown) {
329  Create(false);
330  Play();
331  Pause();
332}
333
334TEST_F(AudioRendererHostTest, SimulateError) {
335  Create(false);
336  Play();
337  SimulateError();
338}
339
340// Simulate the case when an error is generated on the browser process,
341// the audio device is closed but the render process try to close the
342// audio stream again.
343TEST_F(AudioRendererHostTest, SimulateErrorAndClose) {
344  Create(false);
345  Play();
346  SimulateError();
347  Close();
348}
349
350TEST_F(AudioRendererHostTest, CreateUnifiedStreamAndClose) {
351  Create(true);
352  Close();
353}
354
355// TODO(hclam): Add tests for data conversation in low latency mode.
356
357}  // namespace content
358