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