Chromium Code Reviews
|
| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2011 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/message_loop.h" | |
| 7 #include "base/path_service.h" | |
| 8 #include "base/process_util.h" | |
| 9 #include "base/synchronization/waitable_event.h" | |
| 10 #include "base/test/test_timeouts.h" | |
| 11 #include "base/time.h" | |
| 12 | |
|
Paweł Hajdan Jr.
2011/11/02 09:29:15
nit: We're not doing gaps like this in the #includ
tommi (sloooow) - chröme
2011/11/02 10:59:23
Done.
| |
| 13 #include "content/browser/mock_resource_context.h" | |
| 14 #include "content/browser/renderer_host/media/audio_renderer_host.h" | |
| 15 #include "content/browser/renderer_host/media/mock_media_observer.h" | |
| 16 #include "content/common/child_process.h" | |
| 17 #include "content/common/child_thread.h" | |
| 18 #include "content/common/media/audio_messages.h" | |
| 19 #include "content/common/view_messages.h" | |
| 20 #include "content/public/common/content_paths.h" | |
| 21 #include "content/renderer/media/audio_renderer_impl.h" | |
| 22 #include "content/renderer/media/webrtc_audio_device_impl.h" | |
| 23 #include "content/renderer/mock_content_renderer_client.h" | |
| 24 #include "content/renderer/render_process.h" | |
| 25 #include "content/renderer/render_thread_impl.h" | |
| 26 #include "content/test/test_browser_thread.h" | |
| 27 | |
| 28 #include "ipc/ipc_channel.h" | |
| 29 | |
| 30 #include "media/audio/audio_util.h" | |
| 31 #include "media/base/data_buffer.h" | |
| 32 #include "media/base/mock_callback.h" | |
| 33 #include "media/base/mock_filter_host.h" | |
| 34 #include "media/base/mock_filters.h" | |
| 35 | |
| 36 #include "net/url_request/url_request_test_util.h" | |
| 37 | |
| 38 #include "testing/gmock/include/gmock/gmock.h" | |
| 39 #include "testing/gtest/include/gtest/gtest.h" | |
| 40 | |
| 41 #include "third_party/webrtc/voice_engine/main/interface/voe_audio_processing.h" | |
| 42 #include "third_party/webrtc/voice_engine/main/interface/voe_base.h" | |
| 43 #include "third_party/webrtc/voice_engine/main/interface/voe_file.h" | |
| 44 #include "third_party/webrtc/voice_engine/main/interface/voe_network.h" | |
| 45 | |
| 46 using testing::_; | |
| 47 using testing::InvokeWithoutArgs; | |
| 48 using testing::Return; | |
| 49 using testing::StrEq; | |
| 50 | |
| 51 namespace { | |
| 52 // This class is a mock of the child process singleton which is needed | |
| 53 // to be able to create a RenderThread object. | |
| 54 class MockRenderProcess : public RenderProcess { | |
| 55 public: | |
| 56 MockRenderProcess() {} | |
| 57 virtual ~MockRenderProcess() {} | |
| 58 | |
| 59 // RenderProcess implementation. | |
| 60 virtual skia::PlatformCanvas* GetDrawingCanvas(TransportDIB** memory, | |
| 61 const gfx::Rect& rect) { return NULL; } | |
| 62 virtual void ReleaseTransportDIB(TransportDIB* memory) {} | |
| 63 virtual bool UseInProcessPlugins() const { return false; } | |
| 64 virtual bool HasInitializedMediaLibrary() const { return false; } | |
| 65 | |
| 66 private: | |
| 67 DISALLOW_COPY_AND_ASSIGN(MockRenderProcess); | |
| 68 }; | |
| 69 | |
| 70 class WebRTCMockResourceContext : public content::ResourceContext { | |
| 71 public: | |
| 72 WebRTCMockResourceContext() {} | |
| 73 virtual ~WebRTCMockResourceContext() {} | |
| 74 virtual void EnsureInitialized() const OVERRIDE {} | |
| 75 }; | |
| 76 | |
| 77 // Convenience class for WebRTC interfaces. Fetches the wrapped interface | |
| 78 // in the constructor via WebRTC's GetInterface mechanism and then releases | |
| 79 // the reference in the destructor. | |
| 80 template<typename T> | |
| 81 class ScopedWebRTCPtr { | |
| 82 public: | |
| 83 template<typename Engine> | |
| 84 explicit ScopedWebRTCPtr(Engine* e) | |
| 85 : ptr_(T::GetInterface(e)) {} | |
| 86 explicit ScopedWebRTCPtr(T* p) : ptr_(p) {} | |
| 87 ~ScopedWebRTCPtr() { reset(); } | |
| 88 T* operator->() const { return ptr_; } | |
| 89 T* get() const { return ptr_; } | |
| 90 | |
| 91 // Releases the current pointer. | |
| 92 void reset() { | |
| 93 if (ptr_) { | |
| 94 ptr_->Release(); | |
| 95 ptr_ = NULL; | |
| 96 } | |
| 97 } | |
| 98 | |
| 99 bool valid() const { return ptr_ != NULL; } | |
| 100 | |
| 101 private: | |
| 102 T* ptr_; | |
| 103 }; | |
| 104 | |
| 105 // Wrapper to automatically calling T::Delete in the destructor. | |
| 106 // This is useful for some WebRTC objects that have their own Create/Delete | |
| 107 // methods and we can't use our our scoped_* classes. | |
| 108 template <typename T> | |
| 109 class AutoDelete { | |
| 110 public: | |
| 111 AutoDelete() : ptr_(NULL) {} | |
| 112 explicit AutoDelete(T* ptr) : ptr_(ptr) {} | |
| 113 ~AutoDelete() { reset(); } | |
| 114 | |
| 115 void reset() { | |
| 116 if (ptr_) { | |
| 117 T::Delete(ptr_); | |
| 118 ptr_ = NULL; | |
| 119 } | |
| 120 } | |
| 121 | |
| 122 T* operator->() { return ptr_; } | |
| 123 T* get() const { return ptr_; } | |
| 124 | |
| 125 bool valid() const { return ptr_ != NULL; } | |
| 126 | |
| 127 protected: | |
| 128 T* ptr_; | |
| 129 }; | |
| 130 | |
| 131 // This task signals an event owned by the task owner when run and quits. | |
| 132 class SignalTask : public Task { | |
|
Paweł Hajdan Jr.
2011/11/02 09:29:15
We already have a class like this (chrome/test/bas
tommi (sloooow) - chröme
2011/11/02 10:59:23
http://codereview.chromium.org/8438034/
| |
| 133 public: | |
| 134 explicit SignalTask(base::WaitableEvent* event) : event_(event) {} | |
| 135 virtual ~SignalTask() {} | |
| 136 virtual void Run() { event_->Signal(); } | |
| 137 private: | |
| 138 base::WaitableEvent* event_; | |
| 139 DISALLOW_COPY_AND_ASSIGN(SignalTask); | |
| 140 }; | |
| 141 | |
| 142 // Quits the current message loop when run and also sets a supplied bool | |
| 143 // to true. If the task never ran, the bool should remain false. | |
| 144 class QuitAndRaiseTask : public Task { | |
| 145 public: | |
| 146 explicit QuitAndRaiseTask(bool* was_run) : was_run_(was_run) { | |
| 147 *was_run = false; | |
| 148 } | |
| 149 | |
| 150 virtual void Run() { | |
| 151 *was_run_ = true; | |
| 152 MessageLoop::current()->Quit(); | |
| 153 } | |
| 154 protected: | |
| 155 bool* was_run_; | |
| 156 }; | |
| 157 | |
| 158 ACTION_P(QuitMessageLoop, loop_or_proxy) { | |
| 159 LOG(WARNING) << __FUNCTION__; | |
| 160 loop_or_proxy->PostTask(FROM_HERE, new MessageLoop::QuitTask()); | |
| 161 } | |
| 162 | |
| 163 } // end namespace | |
| 164 | |
| 165 // IPC::Channel::Listener implementation for the WebRTCAudioDeviceTest test | |
| 166 // fixture. Provides basic implementations for initialization tasks and | |
| 167 // forwards callbacks to audio ipc filters. | |
| 168 template <class T> | |
| 169 class WebRTCAudioDeviceTestChannelListener : public IPC::Channel::Listener { | |
| 170 public: | |
| 171 WebRTCAudioDeviceTestChannelListener() {} | |
| 172 ~WebRTCAudioDeviceTestChannelListener() {} | |
| 173 | |
| 174 void OnGetHardwareSampleRate(double* sample_rate) { | |
| 175 *sample_rate = media::GetAudioHardwareSampleRate(); | |
| 176 } | |
| 177 | |
| 178 void OnGetHardwareInputSampleRate(double* sample_rate) { | |
| 179 *sample_rate = media::GetAudioInputHardwareSampleRate(); | |
| 180 } | |
| 181 | |
| 182 bool Send(IPC::Message* message) { | |
| 183 return channel_->Send(message); | |
| 184 } | |
| 185 | |
| 186 // IPC::Channel::Listener implementation. | |
| 187 virtual bool OnMessageReceived(const IPC::Message& message) { | |
| 188 T* me = static_cast<T*>(this); | |
| 189 if (me->render_thread()) { | |
| 190 IPC::ChannelProxy::MessageFilter* filter = | |
| 191 me->render_thread()->audio_input_message_filter(); | |
| 192 if (filter->OnMessageReceived(message)) | |
| 193 return true; | |
| 194 | |
| 195 filter = me->render_thread()->audio_message_filter(); | |
| 196 if (filter->OnMessageReceived(message)) | |
| 197 return true; | |
| 198 } | |
| 199 | |
| 200 if (audio_render_host_.get()) { | |
| 201 bool message_was_ok = false; | |
| 202 if (audio_render_host_->OnMessageReceived(message, &message_was_ok)) | |
| 203 return true; | |
| 204 } | |
| 205 | |
| 206 bool handled = true; | |
| 207 IPC_BEGIN_MESSAGE_MAP(WebRTCAudioDeviceTest, message) | |
|
Paweł Hajdan Jr.
2011/11/02 09:29:15
Please use MESSAGE_MAP_EX to avoid an implicit DCH
tommi (sloooow) - chröme
2011/11/02 10:59:23
Done.
| |
| 208 IPC_MESSAGE_HANDLER(ViewHostMsg_GetHardwareSampleRate, | |
| 209 OnGetHardwareSampleRate) | |
| 210 IPC_MESSAGE_HANDLER(ViewHostMsg_GetHardwareInputSampleRate, | |
| 211 OnGetHardwareInputSampleRate) | |
| 212 IPC_MESSAGE_UNHANDLED(handled = false) | |
| 213 IPC_END_MESSAGE_MAP() | |
| 214 | |
| 215 if (!handled) { | |
| 216 DLOG(WARNING) << "Unhandled IPC message"; | |
|
Paweł Hajdan Jr.
2011/11/02 09:29:15
Don't you want to make the test fail in that case?
tommi (sloooow) - chröme
2011/11/02 10:59:23
I added this comment:
if (!handled) {
/
| |
| 217 } | |
| 218 | |
| 219 return true; | |
| 220 } | |
| 221 | |
| 222 protected: | |
| 223 void CreateChannel(const char* name, | |
| 224 content::ResourceContext* resource_context) { | |
| 225 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 226 audio_render_host_ = new AudioRendererHost(resource_context); | |
| 227 audio_render_host_->OnChannelConnected(base::GetCurrentProcId()); | |
| 228 | |
| 229 channel_.reset(new IPC::Channel(name, IPC::Channel::MODE_SERVER, this)); | |
| 230 ASSERT_TRUE(channel_->Connect()); | |
| 231 | |
| 232 audio_render_host_->OnFilterAdded(channel_.get()); | |
| 233 } | |
| 234 | |
| 235 void DestroyChannel() { | |
| 236 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 237 channel_.reset(); | |
| 238 audio_render_host_ = NULL; | |
| 239 } | |
| 240 | |
| 241 scoped_ptr<IPC::Channel> channel_; | |
| 242 scoped_refptr<AudioRendererHost> audio_render_host_; | |
| 243 }; | |
| 244 | |
| 245 class WebRTCAudioDeviceTest | |
| 246 : public ::testing::Test, | |
| 247 public WebRTCAudioDeviceTestChannelListener<WebRTCAudioDeviceTest> { | |
| 248 public: | |
| 249 class SetupTask : public base::RefCountedThreadSafe<SetupTask> { | |
| 250 public: | |
| 251 explicit SetupTask(WebRTCAudioDeviceTest* test) : test_(test) {} | |
| 252 void InitializeIOThread(const char* thread_name) { | |
| 253 test_->InitializeIOThread(thread_name); | |
| 254 } | |
| 255 void UninitializeIOThread() { test_->UninitializeIOThread(); } | |
| 256 protected: | |
| 257 WebRTCAudioDeviceTest* test_; | |
| 258 }; | |
| 259 | |
| 260 WebRTCAudioDeviceTest() : render_thread_(NULL), event_(false, false) {} | |
| 261 virtual ~WebRTCAudioDeviceTest() {} | |
| 262 | |
| 263 static void SetUpTestCase() { | |
|
Paweł Hajdan Jr.
2011/11/02 09:29:15
Shouldn't you have a corresponding TearDownTestCas
tommi (sloooow) - chröme
2011/11/02 10:59:23
This fixture is based in parts on the AudioRendere
| |
| 264 // Set low latency mode, as it soon would be on by default. | |
| 265 if (AudioRendererImpl::latency_type() == | |
| 266 AudioRendererImpl::kUninitializedLatency) { | |
| 267 AudioRendererImpl::set_latency_type(AudioRendererImpl::kLowLatency); | |
| 268 } | |
| 269 DCHECK_EQ(AudioRendererImpl::kLowLatency, | |
| 270 AudioRendererImpl::latency_type()); | |
| 271 } | |
| 272 | |
| 273 virtual void SetUp() { | |
| 274 // This part sets up a RenderThread environment to ensure that | |
| 275 // RenderThread::current() (<=> TLS pointer) is valid. | |
| 276 // Main parts are inspired by the RenderViewFakeResourcesTest. | |
| 277 // Note that, the IPC part is not utilized in this test. | |
| 278 content::GetContentClient()->set_renderer(&mock_content_renderer_client_); | |
|
Paweł Hajdan Jr.
2011/11/02 09:29:15
Is this undone in TearDown? Please use a scoped ob
tommi (sloooow) - chröme
2011/11/02 10:59:23
This is another thing that is borrowed from AudioR
| |
| 279 mock_process_.reset(new MockRenderProcess); | |
| 280 ui_thread_.reset(new content::TestBrowserThread(BrowserThread::UI, | |
| 281 MessageLoop::current())); | |
| 282 | |
| 283 // Construct the resource context on the UI thread. | |
| 284 resource_context_.reset(new WebRTCMockResourceContext()); | |
| 285 | |
| 286 static const char kThreadName[] = "RenderThread"; | |
| 287 ChildProcess::current()->io_message_loop()->PostTask( | |
| 288 FROM_HERE, | |
| 289 base::Bind(&SetupTask::InitializeIOThread, new SetupTask(this), | |
| 290 kThreadName)); | |
| 291 WaitForIOThreadCompletion(); | |
| 292 | |
| 293 render_thread_ = new RenderThreadImpl(kThreadName); | |
| 294 mock_process_->set_main_thread(render_thread_); | |
| 295 } | |
| 296 | |
| 297 virtual void TearDown() { | |
| 298 ChildProcess::current()->io_message_loop()->PostTask( | |
| 299 FROM_HERE, | |
| 300 base::Bind(&SetupTask::UninitializeIOThread, new SetupTask(this))); | |
| 301 WaitForIOThreadCompletion(); | |
| 302 mock_process_.reset(); | |
| 303 } | |
| 304 | |
| 305 RenderThreadImpl* render_thread() const { | |
| 306 return render_thread_; | |
| 307 } | |
| 308 | |
| 309 protected: | |
| 310 void InitializeIOThread(const char* thread_name) { | |
| 311 // Set the current thread as the IO thread. | |
| 312 io_thread_.reset(new content::TestBrowserThread(BrowserThread::IO, | |
| 313 MessageLoop::current())); | |
| 314 test_request_context_ = new TestURLRequestContext(); | |
| 315 resource_context_->set_request_context(test_request_context_.get()); | |
| 316 media_observer_.reset(new MockMediaObserver()); | |
| 317 resource_context_->set_media_observer(media_observer_.get()); | |
| 318 | |
| 319 CreateChannel(thread_name, resource_context_.get()); | |
| 320 } | |
| 321 | |
| 322 void UninitializeIOThread() { | |
| 323 DestroyChannel(); | |
| 324 resource_context_.reset(); | |
| 325 test_request_context_ = NULL; | |
| 326 } | |
| 327 | |
| 328 // Posts a final task to the IO message loop and waits for completion. | |
| 329 void WaitForIOThreadCompletion() { | |
| 330 ChildProcess::current()->io_message_loop()->PostTask( | |
| 331 FROM_HERE, new SignalTask(&event_)); | |
| 332 EXPECT_TRUE(event_.TimedWait( | |
| 333 base::TimeDelta::FromMilliseconds(TestTimeouts::action_timeout_ms()))); | |
| 334 } | |
| 335 | |
| 336 // Convenience getter for gmock. | |
| 337 MockMediaObserver& media_observer() const { | |
| 338 return *media_observer_.get(); | |
| 339 } | |
| 340 | |
| 341 std::string GetTestDataPath(const FilePath::StringType& file_name) { | |
| 342 FilePath path; | |
| 343 PathService::Get(content::DIR_TEST_DATA, &path); | |
|
Paweł Hajdan Jr.
2011/11/02 09:29:15
Check the return value.
tommi (sloooow) - chröme
2011/11/02 10:59:23
Done.
| |
| 344 path = path.Append(file_name); | |
| 345 #ifdef OS_WIN | |
| 346 return WideToUTF8(path.value()); | |
| 347 #else | |
| 348 return path.value(); | |
| 349 #endif | |
| 350 } | |
| 351 | |
| 352 // Actual implementation of the short and long PlayFile tests. | |
| 353 // If the |duration| parameter is 0, then the function determines the length | |
| 354 // of the audio file and plays the entire file. Otherwise, |duration| | |
| 355 // specifies the number of milliseconds to allow the file to play. | |
| 356 void PlayLocalFile(int duration); | |
| 357 | |
| 358 MessageLoopForUI message_loop_; | |
| 359 content::MockContentRendererClient mock_content_renderer_client_; | |
| 360 RenderThreadImpl* render_thread_; // owned by mock_process_ | |
| 361 scoped_ptr<MockRenderProcess> mock_process_; | |
| 362 media::MockFilterHost host_; | |
| 363 base::WaitableEvent event_; | |
| 364 scoped_ptr<MockMediaObserver> media_observer_; | |
| 365 scoped_ptr<content::ResourceContext> resource_context_; | |
| 366 scoped_refptr<net::URLRequestContext> test_request_context_; | |
| 367 | |
| 368 // Initialized on the main test thread that we mark as the UI thread. | |
| 369 scoped_ptr<content::TestBrowserThread> ui_thread_; | |
| 370 // Initialized on our IO thread to satisfy BrowserThread::IO checks. | |
| 371 scoped_ptr<content::TestBrowserThread> io_thread_; | |
| 372 | |
| 373 private: | |
| 374 DISALLOW_COPY_AND_ASSIGN(WebRTCAudioDeviceTest); | |
|
Paweł Hajdan Jr.
2011/11/02 09:29:15
nit: No need for that in a test fixture.
tommi (sloooow) - chröme
2011/11/02 10:59:23
Done.
| |
| 375 }; | |
| 376 | |
| 377 // A very basic implementation of webrtc::Transport that acts as a transport | |
| 378 // but just forwards all calls to a local webrtc::VoENetwork implementation. | |
| 379 // Ownership of the VoENetwork object lies outside the class. | |
| 380 class TransportImpl : public webrtc::Transport { | |
| 381 public: | |
| 382 explicit TransportImpl(webrtc::VoENetwork* network) : network_(network) {} | |
| 383 ~TransportImpl() {} | |
| 384 | |
| 385 virtual int SendPacket(int channel, const void* data, int len) { | |
| 386 DVLOG(1) << __FUNCTION__; | |
| 387 return network_->ReceivedRTPPacket(channel, data, len); | |
| 388 } | |
| 389 | |
| 390 virtual int SendRTCPPacket(int channel, const void* data, int len) { | |
| 391 DVLOG(1) << __FUNCTION__; | |
| 392 return network_->ReceivedRTCPPacket(channel, data, len); | |
| 393 } | |
| 394 | |
| 395 private: | |
| 396 webrtc::VoENetwork* network_; | |
| 397 }; | |
| 398 | |
| 399 // Basic test that instantiates and initializes an instance of | |
| 400 // WebRtcAudioDeviceImpl. | |
| 401 TEST_F(WebRTCAudioDeviceTest, Construct) { | |
| 402 scoped_refptr<WebRtcAudioDeviceImpl> audio_device( | |
| 403 new WebRtcAudioDeviceImpl()); | |
| 404 audio_device->SetSessionId(1); | |
| 405 | |
| 406 AutoDelete<webrtc::VoiceEngine> engine(webrtc::VoiceEngine::Create()); | |
| 407 | |
| 408 ScopedWebRTCPtr<webrtc::VoEBase> base(engine.get()); | |
| 409 int err = base->Init(audio_device); | |
| 410 EXPECT_EQ(0, err); | |
| 411 } | |
| 412 | |
| 413 void WebRTCAudioDeviceTest::PlayLocalFile(int duration) { | |
| 414 EXPECT_GE(duration, 0); | |
| 415 EXPECT_CALL(media_observer(), | |
| 416 OnSetAudioStreamStatus(_, 1, StrEq("created"))).Times(1); | |
| 417 | |
| 418 EXPECT_CALL(media_observer(), | |
| 419 OnSetAudioStreamPlaying(_, 1, true)).Times(1); | |
| 420 | |
| 421 // When the "closed" event is triggered, we end the test. | |
| 422 EXPECT_CALL(media_observer(), | |
| 423 OnSetAudioStreamStatus(_, 1, StrEq("closed"))) | |
| 424 .WillOnce(QuitMessageLoop(message_loop_.message_loop_proxy())); | |
| 425 | |
| 426 EXPECT_CALL(media_observer(), | |
| 427 OnDeleteAudioStream(_, 1)).Times(1); | |
| 428 | |
| 429 scoped_refptr<WebRtcAudioDeviceImpl> audio_device( | |
| 430 new WebRtcAudioDeviceImpl()); | |
| 431 audio_device->SetSessionId(1); | |
| 432 | |
| 433 AutoDelete<webrtc::VoiceEngine> engine(webrtc::VoiceEngine::Create()); | |
| 434 | |
| 435 ScopedWebRTCPtr<webrtc::VoEBase> base(engine.get()); | |
| 436 int err = base->Init(audio_device); | |
| 437 EXPECT_EQ(0, err); | |
| 438 if (err == 0) { | |
| 439 ScopedWebRTCPtr<webrtc::VoEAudioProcessing> audio_processing(engine.get()); | |
| 440 EXPECT_EQ(0, audio_processing->SetAgcStatus(true, | |
| 441 webrtc::kAgcAdaptiveDigital)); | |
| 442 | |
| 443 int ch = base->CreateChannel(); | |
| 444 EXPECT_NE(-1, ch); | |
| 445 | |
| 446 ScopedWebRTCPtr<webrtc::VoENetwork> network(engine.get()); | |
| 447 scoped_ptr<TransportImpl> transport(new TransportImpl(network.get())); | |
| 448 EXPECT_EQ(0, network->RegisterExternalTransport(ch, *transport.get())); | |
| 449 EXPECT_EQ(0, base->StartReceive(ch)); | |
| 450 EXPECT_EQ(0, base->StartPlayout(ch)); | |
| 451 EXPECT_EQ(0, base->StartSend(ch)); | |
| 452 | |
| 453 std::string file_path( | |
| 454 GetTestDataPath(FILE_PATH_LITERAL("speechmusic_mono_16kHz.pcm"))); | |
| 455 | |
| 456 ScopedWebRTCPtr<webrtc::VoEFile> file(engine.get()); | |
| 457 if (duration == 0) { | |
| 458 EXPECT_EQ(0, file->GetFileDuration(file_path.c_str(), duration, | |
| 459 webrtc::kFileFormatPcm16kHzFile)); | |
| 460 EXPECT_NE(0, duration); | |
| 461 } | |
| 462 | |
| 463 EXPECT_EQ(0, file->StartPlayingFileLocally(ch, file_path.c_str(), false, | |
| 464 webrtc::kFileFormatPcm16kHzFile)); | |
| 465 | |
| 466 message_loop_.PostDelayedTask(FROM_HERE, | |
| 467 new MessageLoop::QuitTask(), duration); | |
| 468 message_loop_.Run(); | |
| 469 | |
| 470 EXPECT_EQ(0, network->DeRegisterExternalTransport(ch)); | |
| 471 } | |
| 472 } | |
| 473 | |
| 474 // Plays a local file. This test usually takes just under a minute to run | |
| 475 // and requires an audio card to work, so disabled by default. | |
| 476 TEST_F(WebRTCAudioDeviceTest, DISABLED_PlayLocalFileLong) { | |
| 477 PlayLocalFile(0); | |
| 478 } | |
| 479 | |
| 480 TEST_F(WebRTCAudioDeviceTest, PlayLocalFile) { | |
| 481 PlayLocalFile(TestTimeouts::action_timeout_ms()); | |
| 482 } | |
| OLD | NEW |