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/environment.h" 7 #include "base/memory/scoped_ptr.h" 8 #include "base/message_loop/message_loop.h" 9 #include "base/sync_socket.h" 10 #include "content/browser/browser_thread_impl.h" 11 #include "content/browser/renderer_host/media/audio_input_device_manager.h" 12 #include "content/browser/renderer_host/media/audio_mirroring_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/browser/renderer_host/media/mock_media_observer.h" 16 #include "content/common/media/audio_messages.h" 17 #include "content/common/media/media_stream_options.h" 18 #include "ipc/ipc_message_utils.h" 19 #include "media/audio/audio_manager.h" 20 #include "media/audio/audio_manager_base.h" 21 #include "media/audio/fake_audio_output_stream.h" 22 #include "net/url_request/url_request_context.h" 23 #include "testing/gmock/include/gmock/gmock.h" 24 #include "testing/gtest/include/gtest/gtest.h" 25 26 using ::testing::_; 27 using ::testing::Assign; 28 using ::testing::DoAll; 29 using ::testing::NotNull; 30 31 namespace content { 32 33 static const int kRenderProcessId = 1; 34 static const int kRenderViewId = 4; 35 static const int kStreamId = 50; 36 37 class MockAudioMirroringManager : public AudioMirroringManager { 38 public: 39 MockAudioMirroringManager() {} 40 virtual ~MockAudioMirroringManager() {} 41 42 MOCK_METHOD3(AddDiverter, 43 void(int render_process_id, int render_view_id, 44 Diverter* diverter)); 45 MOCK_METHOD3(RemoveDiverter, 46 void(int render_process_id, int render_view_id, 47 Diverter* diverter)); 48 49 private: 50 DISALLOW_COPY_AND_ASSIGN(MockAudioMirroringManager); 51 }; 52 53 class MockAudioRendererHost : public AudioRendererHost { 54 public: 55 explicit MockAudioRendererHost( 56 media::AudioManager* audio_manager, 57 AudioMirroringManager* mirroring_manager, 58 MediaInternals* media_internals, 59 MediaStreamManager* media_stream_manager) 60 : AudioRendererHost(kRenderProcessId, 61 audio_manager, 62 mirroring_manager, 63 media_internals, 64 media_stream_manager), 65 shared_memory_length_(0) { 66 } 67 68 // A list of mock methods. 69 MOCK_METHOD2(OnStreamCreated, 70 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, int stream_id, 104 base::SharedMemoryHandle handle, 105 #if defined(OS_WIN) 106 base::SyncSocket::Handle socket_handle, 107 #else 108 base::FileDescriptor socket_descriptor, 109 #endif 110 uint32 length) { 111 // Maps the shared memory. 112 shared_memory_.reset(new base::SharedMemory(handle, false)); 113 CHECK(shared_memory_->Map(length)); 114 CHECK(shared_memory_->memory()); 115 shared_memory_length_ = length; 116 117 // Create the SyncSocket using the handle. 118 base::SyncSocket::Handle sync_socket_handle; 119 #if defined(OS_WIN) 120 sync_socket_handle = socket_handle; 121 #else 122 sync_socket_handle = socket_descriptor.fd; 123 #endif 124 sync_socket_.reset(new base::SyncSocket(sync_socket_handle)); 125 126 // And then delegate the call to the mock method. 127 OnStreamCreated(stream_id, length); 128 } 129 130 void OnStreamStateChanged(const IPC::Message& msg, int stream_id, 131 media::AudioOutputIPCDelegate::State state) { 132 switch (state) { 133 case media::AudioOutputIPCDelegate::kPlaying: 134 OnStreamPlaying(stream_id); 135 break; 136 case media::AudioOutputIPCDelegate::kPaused: 137 OnStreamPaused(stream_id); 138 break; 139 case media::AudioOutputIPCDelegate::kError: 140 OnStreamError(stream_id); 141 break; 142 default: 143 FAIL() << "Unknown stream state"; 144 break; 145 } 146 } 147 148 scoped_ptr<base::SharedMemory> shared_memory_; 149 scoped_ptr<base::SyncSocket> sync_socket_; 150 uint32 shared_memory_length_; 151 152 DISALLOW_COPY_AND_ASSIGN(MockAudioRendererHost); 153 }; 154 155 ACTION_P(QuitMessageLoop, message_loop) { 156 message_loop->PostTask(FROM_HERE, base::MessageLoop::QuitClosure()); 157 } 158 159 class AudioRendererHostTest : public testing::Test { 160 public: 161 AudioRendererHostTest() : is_stream_active_(false) {} 162 163 protected: 164 virtual void SetUp() { 165 // Create a message loop so AudioRendererHost can use it. 166 message_loop_.reset(new base::MessageLoop(base::MessageLoop::TYPE_IO)); 167 168 // Claim to be on both the UI and IO threads to pass all the DCHECKS. 169 io_thread_.reset(new BrowserThreadImpl(BrowserThread::IO, 170 message_loop_.get())); 171 ui_thread_.reset(new BrowserThreadImpl(BrowserThread::UI, 172 message_loop_.get())); 173 audio_manager_.reset(media::AudioManager::Create()); 174 media_stream_manager_.reset(new MediaStreamManager(audio_manager_.get())); 175 media_stream_manager_->UseFakeDevice(); 176 observer_.reset(new MockMediaInternals()); 177 host_ = new MockAudioRendererHost( 178 audio_manager_.get(), &mirroring_manager_, observer_.get(), 179 media_stream_manager_.get()); 180 181 // Simulate IPC channel connected. 182 host_->OnChannelConnected(base::GetCurrentProcId()); 183 } 184 185 virtual void TearDown() { 186 // Simulate closing the IPC channel. 187 host_->OnChannelClosing(); 188 189 // Release the reference to the mock object. The object will be destructed 190 // on message_loop_. 191 host_ = NULL; 192 193 // We need to continue running message_loop_ to complete all destructions. 194 SyncWithAudioThread(); 195 audio_manager_.reset(); 196 197 // Make sure the stream has been deleted before continuing. 198 while (is_stream_active_) 199 message_loop_->Run(); 200 201 io_thread_.reset(); 202 ui_thread_.reset(); 203 204 // Delete the IO message loop. This will cause the MediaStreamManager to be 205 // notified so it will stop its device thread and device managers. 206 message_loop_.reset(); 207 } 208 209 void Create(bool unified_stream) { 210 EXPECT_CALL(*observer_, 211 OnSetAudioStreamStatus(_, kStreamId, "created")); 212 EXPECT_CALL(*host_.get(), OnStreamCreated(kStreamId, _)) 213 .WillOnce(DoAll(Assign(&is_stream_active_, true), 214 QuitMessageLoop(message_loop_.get()))); 215 EXPECT_CALL(mirroring_manager_, 216 AddDiverter(kRenderProcessId, kRenderViewId, NotNull())) 217 .RetiresOnSaturation(); 218 219 // Send a create stream message to the audio output stream and wait until 220 // we receive the created message. 221 int session_id; 222 media::AudioParameters params; 223 if (unified_stream) { 224 // Use AudioInputDeviceManager::kFakeOpenSessionId as the session id to 225 // pass the permission check. 226 session_id = AudioInputDeviceManager::kFakeOpenSessionId; 227 params = media::AudioParameters( 228 media::AudioParameters::AUDIO_FAKE, 229 media::CHANNEL_LAYOUT_STEREO, 230 2, 231 media::AudioParameters::kAudioCDSampleRate, 16, 232 media::AudioParameters::kAudioCDSampleRate / 10); 233 } else { 234 session_id = 0; 235 params = media::AudioParameters( 236 media::AudioParameters::AUDIO_FAKE, 237 media::CHANNEL_LAYOUT_STEREO, 238 media::AudioParameters::kAudioCDSampleRate, 16, 239 media::AudioParameters::kAudioCDSampleRate / 10); 240 } 241 host_->OnCreateStream(kStreamId, kRenderViewId, session_id, params); 242 message_loop_->Run(); 243 244 // At some point in the future, a corresponding RemoveDiverter() call must 245 // be made. 246 EXPECT_CALL(mirroring_manager_, 247 RemoveDiverter(kRenderProcessId, kRenderViewId, NotNull())) 248 .RetiresOnSaturation(); 249 250 // All created streams should ultimately be closed. 251 EXPECT_CALL(*observer_, 252 OnSetAudioStreamStatus(_, kStreamId, "closed")); 253 254 // Expect the audio stream will be deleted at some later point. 255 EXPECT_CALL(*observer_, OnDeleteAudioStream(_, kStreamId)) 256 .WillOnce(DoAll(Assign(&is_stream_active_, false), 257 QuitMessageLoop(message_loop_.get()))); 258 } 259 260 void Close() { 261 // Send a message to AudioRendererHost to tell it we want to close the 262 // stream. 263 host_->OnCloseStream(kStreamId); 264 if (is_stream_active_) 265 message_loop_->Run(); 266 else 267 message_loop_->RunUntilIdle(); 268 } 269 270 void Play() { 271 EXPECT_CALL(*observer_, 272 OnSetAudioStreamPlaying(_, kStreamId, true)); 273 EXPECT_CALL(*host_.get(), OnStreamPlaying(kStreamId)) 274 .WillOnce(QuitMessageLoop(message_loop_.get())); 275 276 host_->OnPlayStream(kStreamId); 277 message_loop_->Run(); 278 } 279 280 void Pause() { 281 EXPECT_CALL(*observer_, 282 OnSetAudioStreamPlaying(_, kStreamId, false)); 283 EXPECT_CALL(*host_.get(), OnStreamPaused(kStreamId)) 284 .WillOnce(QuitMessageLoop(message_loop_.get())); 285 286 host_->OnPauseStream(kStreamId); 287 message_loop_->Run(); 288 } 289 290 void SetVolume(double volume) { 291 EXPECT_CALL(*observer_, 292 OnSetAudioStreamVolume(_, kStreamId, volume)); 293 294 host_->OnSetVolume(kStreamId, volume); 295 message_loop_->RunUntilIdle(); 296 } 297 298 void SimulateError() { 299 EXPECT_CALL(*observer_, 300 OnSetAudioStreamStatus(_, kStreamId, "error")); 301 EXPECT_EQ(1u, host_->audio_entries_.size()) 302 << "Calls Create() before calling this method"; 303 304 // Expect an error signal sent through IPC. 305 EXPECT_CALL(*host_.get(), OnStreamError(kStreamId)); 306 307 // Simulate an error sent from the audio device. 308 host_->ReportErrorAndClose(kStreamId); 309 SyncWithAudioThread(); 310 311 // Expect the audio stream record is removed. 312 EXPECT_EQ(0u, host_->audio_entries_.size()); 313 } 314 315 // Called on the audio thread. 316 static void PostQuitMessageLoop(base::MessageLoop* message_loop) { 317 message_loop->PostTask(FROM_HERE, base::MessageLoop::QuitClosure()); 318 } 319 320 // Called on the main thread. 321 static void PostQuitOnAudioThread(media::AudioManager* audio_manager, 322 base::MessageLoop* message_loop) { 323 audio_manager->GetMessageLoop()->PostTask(FROM_HERE, 324 base::Bind(&PostQuitMessageLoop, message_loop)); 325 } 326 327 // SyncWithAudioThread() waits until all pending tasks on the audio thread 328 // are executed while also processing pending task in message_loop_ on the 329 // current thread. It is used to synchronize with the audio thread when we are 330 // closing an audio stream. 331 void SyncWithAudioThread() { 332 // Don't use scoped_refptr to addref the media::AudioManager when posting 333 // to the thread that itself owns. 334 message_loop_->PostTask( 335 FROM_HERE, base::Bind(&PostQuitOnAudioThread, 336 base::Unretained(audio_manager_.get()), 337 message_loop_.get())); 338 message_loop_->Run(); 339 } 340 341 private: 342 scoped_ptr<MockMediaInternals> observer_; 343 MockAudioMirroringManager mirroring_manager_; 344 scoped_refptr<MockAudioRendererHost> host_; 345 scoped_ptr<base::MessageLoop> message_loop_; 346 scoped_ptr<BrowserThreadImpl> io_thread_; 347 scoped_ptr<BrowserThreadImpl> ui_thread_; 348 scoped_ptr<media::AudioManager> audio_manager_; 349 scoped_ptr<MediaStreamManager> media_stream_manager_; 350 351 bool is_stream_active_; 352 353 DISALLOW_COPY_AND_ASSIGN(AudioRendererHostTest); 354 }; 355 356 TEST_F(AudioRendererHostTest, CreateAndClose) { 357 Create(false); 358 Close(); 359 } 360 361 // Simulate the case where a stream is not properly closed. 362 TEST_F(AudioRendererHostTest, CreateAndShutdown) { 363 Create(false); 364 } 365 366 TEST_F(AudioRendererHostTest, CreatePlayAndClose) { 367 Create(false); 368 Play(); 369 Close(); 370 } 371 372 TEST_F(AudioRendererHostTest, CreatePlayPauseAndClose) { 373 Create(false); 374 Play(); 375 Pause(); 376 Close(); 377 } 378 379 TEST_F(AudioRendererHostTest, SetVolume) { 380 Create(false); 381 SetVolume(0.5); 382 Play(); 383 Pause(); 384 Close(); 385 } 386 387 // Simulate the case where a stream is not properly closed. 388 TEST_F(AudioRendererHostTest, CreatePlayAndShutdown) { 389 Create(false); 390 Play(); 391 } 392 393 // Simulate the case where a stream is not properly closed. 394 TEST_F(AudioRendererHostTest, CreatePlayPauseAndShutdown) { 395 Create(false); 396 Play(); 397 Pause(); 398 } 399 400 TEST_F(AudioRendererHostTest, SimulateError) { 401 Create(false); 402 Play(); 403 SimulateError(); 404 } 405 406 // Simulate the case when an error is generated on the browser process, 407 // the audio device is closed but the render process try to close the 408 // audio stream again. 409 TEST_F(AudioRendererHostTest, SimulateErrorAndClose) { 410 Create(false); 411 Play(); 412 SimulateError(); 413 Close(); 414 } 415 416 TEST_F(AudioRendererHostTest, CreateUnifiedStreamAndClose) { 417 Create(true); 418 Close(); 419 } 420 421 // TODO(hclam): Add tests for data conversation in low latency mode. 422 423 } // namespace content 424