Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(210)

Side by Side Diff: chrome/browser/renderer_host/audio_renderer_host_unittest.cc

Issue 6532073: Move core pieces of browser\renderer_host to src\content. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 9 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2010 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/environment.h"
6 #include "base/message_loop.h"
7 #include "base/process_util.h"
8 #include "base/scoped_ptr.h"
9 #include "base/sync_socket.h"
10 #include "chrome/browser/browser_thread.h"
11 #include "chrome/browser/renderer_host/audio_renderer_host.h"
12 #include "chrome/common/render_messages.h"
13 #include "chrome/common/render_messages_params.h"
14 #include "ipc/ipc_message_utils.h"
15 #include "media/audio/audio_manager.h"
16 #include "media/audio/fake_audio_output_stream.h"
17 #include "testing/gmock/include/gmock/gmock.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19
20 using ::testing::_;
21 using ::testing::DoAll;
22 using ::testing::InSequence;
23 using ::testing::InvokeWithoutArgs;
24 using ::testing::Return;
25 using ::testing::SaveArg;
26 using ::testing::SetArgumentPointee;
27
28 static const int kInvalidId = -1;
29 static const int kRouteId = 200;
30 static const int kStreamId = 50;
31
32 static bool IsRunningHeadless() {
33 scoped_ptr<base::Environment> env(base::Environment::Create());
34 if (env->HasVar("CHROME_HEADLESS"))
35 return true;
36 return false;
37 }
38
39 class MockAudioRendererHost : public AudioRendererHost {
40 public:
41 MockAudioRendererHost() : shared_memory_length_(0) {
42 }
43
44 virtual ~MockAudioRendererHost() {
45 }
46
47 // A list of mock methods.
48 MOCK_METHOD3(OnRequestPacket,
49 void(int routing_id, int stream_id,
50 AudioBuffersState buffers_state));
51 MOCK_METHOD3(OnStreamCreated,
52 void(int routing_id, int stream_id, int length));
53 MOCK_METHOD3(OnLowLatencyStreamCreated,
54 void(int routing_id, int stream_id, int length));
55 MOCK_METHOD2(OnStreamPlaying, void(int routing_id, int stream_id));
56 MOCK_METHOD2(OnStreamPaused, void(int routing_id, int stream_id));
57 MOCK_METHOD2(OnStreamError, void(int routing_id, int stream_id));
58 MOCK_METHOD3(OnStreamVolume,
59 void(int routing_id, int stream_id, double volume));
60
61 base::SharedMemory* shared_memory() { return shared_memory_.get(); }
62 uint32 shared_memory_length() { return shared_memory_length_; }
63
64 base::SyncSocket* sync_socket() { return sync_socket_.get(); }
65
66 private:
67 // This method is used to dispatch IPC messages to the renderer. We intercept
68 // these messages here and dispatch to our mock methods to verify the
69 // conversation between this object and the renderer.
70 virtual bool Send(IPC::Message* message) {
71 CHECK(message);
72
73 // In this method we dispatch the messages to the according handlers as if
74 // we are the renderer.
75 bool handled = true;
76 IPC_BEGIN_MESSAGE_MAP(MockAudioRendererHost, *message)
77 IPC_MESSAGE_HANDLER(ViewMsg_RequestAudioPacket, OnRequestPacket)
78 IPC_MESSAGE_HANDLER(ViewMsg_NotifyAudioStreamCreated, OnStreamCreated)
79 IPC_MESSAGE_HANDLER(ViewMsg_NotifyLowLatencyAudioStreamCreated,
80 OnLowLatencyStreamCreated)
81 IPC_MESSAGE_HANDLER(ViewMsg_NotifyAudioStreamStateChanged,
82 OnStreamStateChanged)
83 IPC_MESSAGE_HANDLER(ViewMsg_NotifyAudioStreamVolume, OnStreamVolume)
84 IPC_MESSAGE_UNHANDLED(handled = false)
85 IPC_END_MESSAGE_MAP()
86 EXPECT_TRUE(handled);
87
88 delete message;
89 return true;
90 }
91
92 // These handler methods do minimal things and delegate to the mock methods.
93 void OnRequestPacket(const IPC::Message& msg, int stream_id,
94 AudioBuffersState buffers_state) {
95 OnRequestPacket(msg.routing_id(), stream_id, buffers_state);
96 }
97
98 void OnStreamCreated(const IPC::Message& msg, int stream_id,
99 base::SharedMemoryHandle handle, uint32 length) {
100 // Maps the shared memory.
101 shared_memory_.reset(new base::SharedMemory(handle, false));
102 ASSERT_TRUE(shared_memory_->Map(length));
103 ASSERT_TRUE(shared_memory_->memory());
104 shared_memory_length_ = length;
105
106 // And then delegate the call to the mock method.
107 OnStreamCreated(msg.routing_id(), stream_id, length);
108 }
109
110 void OnLowLatencyStreamCreated(const IPC::Message& msg, int stream_id,
111 base::SharedMemoryHandle handle,
112 #if defined(OS_WIN)
113 base::SyncSocket::Handle socket_handle,
114 #else
115 base::FileDescriptor socket_descriptor,
116 #endif
117 uint32 length) {
118 // Maps the shared memory.
119 shared_memory_.reset(new base::SharedMemory(handle, false));
120 CHECK(shared_memory_->Map(length));
121 CHECK(shared_memory_->memory());
122 shared_memory_length_ = length;
123
124 // Create the SyncSocket using the handle.
125 base::SyncSocket::Handle sync_socket_handle;
126 #if defined(OS_WIN)
127 sync_socket_handle = socket_handle;
128 #else
129 sync_socket_handle = socket_descriptor.fd;
130 #endif
131 sync_socket_.reset(new base::SyncSocket(sync_socket_handle));
132
133 // And then delegate the call to the mock method.
134 OnLowLatencyStreamCreated(msg.routing_id(), stream_id, length);
135 }
136
137 void OnStreamStateChanged(const IPC::Message& msg, int stream_id,
138 const ViewMsg_AudioStreamState_Params& params) {
139 if (params.state == ViewMsg_AudioStreamState_Params::kPlaying) {
140 OnStreamPlaying(msg.routing_id(), stream_id);
141 } else if (params.state == ViewMsg_AudioStreamState_Params::kPaused) {
142 OnStreamPaused(msg.routing_id(), stream_id);
143 } else if (params.state == ViewMsg_AudioStreamState_Params::kError) {
144 OnStreamError(msg.routing_id(), stream_id);
145 } else {
146 FAIL() << "Unknown stream state";
147 }
148 }
149
150 void OnStreamVolume(const IPC::Message& msg, int stream_id, double volume) {
151 OnStreamVolume(msg.routing_id(), stream_id, volume);
152 }
153
154 scoped_ptr<base::SharedMemory> shared_memory_;
155 scoped_ptr<base::SyncSocket> sync_socket_;
156 uint32 shared_memory_length_;
157
158 DISALLOW_COPY_AND_ASSIGN(MockAudioRendererHost);
159 };
160
161 ACTION_P(QuitMessageLoop, message_loop) {
162 message_loop->PostTask(FROM_HERE, new MessageLoop::QuitTask());
163 }
164
165 class AudioRendererHostTest : public testing::Test {
166 public:
167 AudioRendererHostTest()
168 : mock_stream_(true) {
169 }
170
171 protected:
172 virtual void SetUp() {
173 // Create a message loop so AudioRendererHost can use it.
174 message_loop_.reset(new MessageLoop(MessageLoop::TYPE_IO));
175 io_thread_.reset(new BrowserThread(BrowserThread::IO, message_loop_.get()));
176 host_ = new MockAudioRendererHost();
177
178 // Simulate IPC channel connected.
179 host_->OnChannelConnected(base::GetCurrentProcId());
180 }
181
182 virtual void TearDown() {
183 // Simulate closing the IPC channel.
184 host_->OnChannelClosing();
185
186 // Release the reference to the mock object. The object will be destructed
187 // on message_loop_.
188 host_ = NULL;
189
190 // We need to continue running message_loop_ to complete all destructions.
191 SyncWithAudioThread();
192
193 io_thread_.reset();
194 }
195
196 void Create() {
197 InSequence s;
198 // 1. We will first receive a OnStreamCreated() signal.
199 EXPECT_CALL(*host_,
200 OnStreamCreated(kRouteId, kStreamId, _));
201
202 // 2. First packet request will arrive.
203 EXPECT_CALL(*host_, OnRequestPacket(kRouteId, kStreamId, _))
204 .WillOnce(QuitMessageLoop(message_loop_.get()));
205
206 IPC::Message msg;
207 msg.set_routing_id(kRouteId);
208
209 ViewHostMsg_Audio_CreateStream_Params params;
210 if (mock_stream_)
211 params.params.format = AudioParameters::AUDIO_MOCK;
212 else
213 params.params.format = AudioParameters::AUDIO_PCM_LINEAR;
214 params.params.channels = 2;
215 params.params.sample_rate = AudioParameters::kAudioCDSampleRate;
216 params.params.bits_per_sample = 16;
217 params.params.samples_per_packet = 0;
218
219 // Send a create stream message to the audio output stream and wait until
220 // we receive the created message.
221 host_->OnCreateStream(msg, kStreamId, params, false);
222 message_loop_->Run();
223 }
224
225 void CreateLowLatency() {
226 InSequence s;
227 // We will first receive a OnLowLatencyStreamCreated() signal.
228 EXPECT_CALL(*host_,
229 OnLowLatencyStreamCreated(kRouteId, kStreamId, _))
230 .WillOnce(QuitMessageLoop(message_loop_.get()));
231
232 IPC::Message msg;
233 msg.set_routing_id(kRouteId);
234
235 ViewHostMsg_Audio_CreateStream_Params params;
236 if (mock_stream_)
237 params.params.format = AudioParameters::AUDIO_MOCK;
238 else
239 params.params.format = AudioParameters::AUDIO_PCM_LINEAR;
240 params.params.channels = 2;
241 params.params.sample_rate = AudioParameters::kAudioCDSampleRate;
242 params.params.bits_per_sample = 16;
243 params.params.samples_per_packet = 0;
244
245 // Send a create stream message to the audio output stream and wait until
246 // we receive the created message.
247 host_->OnCreateStream(msg, kStreamId, params, true);
248 message_loop_->Run();
249 }
250
251 void Close() {
252 // Send a message to AudioRendererHost to tell it we want to close the
253 // stream.
254 IPC::Message msg;
255 msg.set_routing_id(kRouteId);
256 host_->OnCloseStream(msg, kStreamId);
257 message_loop_->RunAllPending();
258 }
259
260 void Play() {
261 EXPECT_CALL(*host_, OnStreamPlaying(kRouteId, kStreamId))
262 .WillOnce(QuitMessageLoop(message_loop_.get()));
263
264 IPC::Message msg;
265 msg.set_routing_id(kRouteId);
266 host_->OnPlayStream(msg, kStreamId);
267 message_loop_->Run();
268 }
269
270 void Pause() {
271 EXPECT_CALL(*host_, OnStreamPaused(kRouteId, kStreamId))
272 .WillOnce(QuitMessageLoop(message_loop_.get()));
273
274 IPC::Message msg;
275 msg.set_routing_id(kRouteId);
276 host_->OnPauseStream(msg, kStreamId);
277 message_loop_->Run();
278 }
279
280 void SetVolume(double volume) {
281 IPC::Message msg;
282 msg.set_routing_id(kRouteId);
283 host_->OnSetVolume(msg, kStreamId, volume);
284 message_loop_->RunAllPending();
285 }
286
287 void NotifyPacketReady() {
288 EXPECT_CALL(*host_, OnRequestPacket(kRouteId, kStreamId, _))
289 .WillOnce(QuitMessageLoop(message_loop_.get()));
290
291 IPC::Message msg;
292 msg.set_routing_id(kRouteId);
293 memset(host_->shared_memory()->memory(), 0, host_->shared_memory_length());
294 host_->OnNotifyPacketReady(msg, kStreamId,
295 host_->shared_memory_length());
296 message_loop_->Run();
297 }
298
299 void SimulateError() {
300 // Find the first AudioOutputController in the AudioRendererHost.
301 CHECK(host_->audio_entries_.size())
302 << "Calls Create() before calling this method";
303 media::AudioOutputController* controller =
304 host_->audio_entries_.begin()->second->controller;
305 CHECK(controller) << "AudioOutputController not found";
306
307 // Expect an error signal sent through IPC.
308 EXPECT_CALL(*host_, OnStreamError(kRouteId, kStreamId));
309
310 // Simulate an error sent from the audio device.
311 host_->OnError(controller, 0);
312 SyncWithAudioThread();
313
314 // Expect the audio stream record is removed.
315 EXPECT_EQ(0u, host_->audio_entries_.size());
316 }
317
318 // Called on the audio thread.
319 static void PostQuitMessageLoop(MessageLoop* message_loop) {
320 message_loop->PostTask(FROM_HERE, new MessageLoop::QuitTask());
321 }
322
323 // Called on the main thread.
324 static void PostQuitOnAudioThread(MessageLoop* message_loop) {
325 AudioManager::GetAudioManager()->GetMessageLoop()->PostTask(
326 FROM_HERE, NewRunnableFunction(&PostQuitMessageLoop, message_loop));
327 }
328
329 // SyncWithAudioThread() waits until all pending tasks on the audio thread
330 // are executed while also processing pending task in message_loop_ on the
331 // current thread. It is used to synchronize with the audio thread when we are
332 // closing an audio stream.
333 void SyncWithAudioThread() {
334 message_loop_->PostTask(
335 FROM_HERE, NewRunnableFunction(&PostQuitOnAudioThread,
336 message_loop_.get()));
337 message_loop_->Run();
338 }
339
340 MessageLoop* message_loop() { return message_loop_.get(); }
341 MockAudioRendererHost* host() { return host_; }
342 void EnableRealDevice() { mock_stream_ = false; }
343
344 private:
345 bool mock_stream_;
346 scoped_refptr<MockAudioRendererHost> host_;
347 scoped_ptr<MessageLoop> message_loop_;
348 scoped_ptr<BrowserThread> io_thread_;
349
350 DISALLOW_COPY_AND_ASSIGN(AudioRendererHostTest);
351 };
352
353 TEST_F(AudioRendererHostTest, CreateAndClose) {
354 if (!IsRunningHeadless())
355 EnableRealDevice();
356
357 Create();
358 Close();
359 }
360
361 TEST_F(AudioRendererHostTest, CreatePlayAndClose) {
362 if (!IsRunningHeadless())
363 EnableRealDevice();
364
365 Create();
366 Play();
367 Close();
368 }
369
370 TEST_F(AudioRendererHostTest, CreatePlayPauseAndClose) {
371 if (!IsRunningHeadless())
372 EnableRealDevice();
373
374 Create();
375 Play();
376 Pause();
377 Close();
378 }
379
380 TEST_F(AudioRendererHostTest, SetVolume) {
381 if (!IsRunningHeadless())
382 EnableRealDevice();
383
384 Create();
385 SetVolume(0.5);
386 Play();
387 Pause();
388 Close();
389
390 // Expect the volume is set.
391 if (IsRunningHeadless()) {
392 EXPECT_EQ(0.5, FakeAudioOutputStream::GetLastFakeStream()->volume());
393 }
394 }
395
396 // Simulate the case where a stream is not properly closed.
397 TEST_F(AudioRendererHostTest, CreateAndShutdown) {
398 if (!IsRunningHeadless())
399 EnableRealDevice();
400
401 Create();
402 }
403
404 // Simulate the case where a stream is not properly closed.
405 TEST_F(AudioRendererHostTest, CreatePlayAndShutdown) {
406 if (!IsRunningHeadless())
407 EnableRealDevice();
408
409 Create();
410 Play();
411 }
412
413 // Simulate the case where a stream is not properly closed.
414 TEST_F(AudioRendererHostTest, CreatePlayPauseAndShutdown) {
415 if (!IsRunningHeadless())
416 EnableRealDevice();
417
418 Create();
419 Play();
420 Pause();
421 }
422
423 TEST_F(AudioRendererHostTest, DataConversationMockStream) {
424 Create();
425
426 // Note that we only do notify three times because the buffer capacity is
427 // triple of one packet size.
428 NotifyPacketReady();
429 NotifyPacketReady();
430 NotifyPacketReady();
431 Close();
432 }
433
434 TEST_F(AudioRendererHostTest, DataConversationRealStream) {
435 if (IsRunningHeadless())
436 return;
437 EnableRealDevice();
438 Create();
439 Play();
440
441 // If this is a real audio device, the data conversation is not limited
442 // to the buffer capacity of AudioOutputController. So we do 5 exchanges
443 // before we close the device.
444 for (int i = 0; i < 5; ++i) {
445 NotifyPacketReady();
446 }
447 Close();
448 }
449
450 TEST_F(AudioRendererHostTest, SimulateError) {
451 if (!IsRunningHeadless())
452 EnableRealDevice();
453
454 Create();
455 Play();
456 SimulateError();
457 }
458
459 // Simulate the case when an error is generated on the browser process,
460 // the audio device is closed but the render process try to close the
461 // audio stream again.
462 TEST_F(AudioRendererHostTest, SimulateErrorAndClose) {
463 if (!IsRunningHeadless())
464 EnableRealDevice();
465
466 Create();
467 Play();
468 SimulateError();
469 Close();
470 }
471
472 TEST_F(AudioRendererHostTest, CreateLowLatencyAndClose) {
473 if (!IsRunningHeadless())
474 EnableRealDevice();
475
476 CreateLowLatency();
477 Close();
478 }
479
480 // Simulate the case where a stream is not properly closed.
481 TEST_F(AudioRendererHostTest, CreateLowLatencyAndShutdown) {
482 if (!IsRunningHeadless())
483 EnableRealDevice();
484
485 CreateLowLatency();
486 }
487
488 // TODO(hclam): Add tests for data conversation in low latency mode.
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698