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