Index: content/renderer/media/audio_renderer_impl_unittest.cc |
=================================================================== |
--- content/renderer/media/audio_renderer_impl_unittest.cc (revision 90962) |
+++ content/renderer/media/audio_renderer_impl_unittest.cc (working copy) |
@@ -2,9 +2,16 @@ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
+#include "base/message_loop.h" |
#include "base/process_util.h" |
+#include "base/synchronization/waitable_event.h" |
+#include "base/time.h" |
#include "content/common/media/audio_messages.h" |
+#include "chrome/renderer/chrome_content_renderer_client.h" |
#include "content/renderer/media/audio_renderer_impl.h" |
+#include "chrome/renderer/mock_render_process.h" |
+#include "content/renderer/render_thread.h" |
+#include "ipc/ipc_channel.h" |
#include "media/base/data_buffer.h" |
#include "media/base/mock_callback.h" |
#include "media/base/mock_filter_host.h" |
@@ -13,19 +20,84 @@ |
using ::testing::Return; |
-class AudioRendererImplTest : public ::testing::Test { |
+// This class defines a set of methods which will be used in combination |
+// with NewRunnableMethod to form tasks which will be posted on the |
+// IO thread. All methods emulate AudioMessageFilter::Delegate calls. |
+class DelegateCaller: public base::RefCountedThreadSafe<DelegateCaller> { |
public: |
- static const int kRouteId = 0; |
+ explicit DelegateCaller(AudioRendererImpl* renderer) |
+ : renderer_(renderer) {} |
+ |
+ void OnCreated(base::SharedMemoryHandle handle, uint32 length) { |
+ renderer_->OnCreated(handle, length); |
+ } |
+ void OnStateChanged(AudioStreamState state) { |
+ renderer_->OnStateChanged(state); |
+ } |
+ void OnRequestPacket(AudioBuffersState buffers_state) { |
+ renderer_->OnRequestPacket(buffers_state); |
+ } |
+ void OnVolume(double volume) { |
+ renderer_->OnVolume(volume); |
+ } |
+ void DestroyCurrentMessageLoop() { |
+ renderer_->WillDestroyCurrentMessageLoop(); |
+ } |
+ private: |
+ friend class base::RefCountedThreadSafe<DelegateCaller>; |
+ ~DelegateCaller() {} |
+ |
+ scoped_refptr<AudioRendererImpl> renderer_; |
+ DISALLOW_COPY_AND_ASSIGN(DelegateCaller); |
+}; |
+ |
+// This task can be posted on the IO thread and will signal an event when |
+// done. The caller can then wait for this signal to ensure that no |
+// additional tasks remain in the task queue. |
+class WaitTask : public Task { |
+ public: |
+ explicit WaitTask(base::WaitableEvent* event) |
+ : event_(event) {} |
+ virtual ~WaitTask() {} |
+ virtual void Run() { |
+ event_->Signal(); |
+ } |
+ |
+ private: |
+ base::WaitableEvent* event_; |
+ DISALLOW_COPY_AND_ASSIGN(WaitTask); |
+}; |
+ |
+class AudioRendererImplTest : public ::testing::Test, |
+ public IPC::Channel::Listener { |
+ public: |
+ // IPC::Channel::Listener implementation. |
+ virtual bool OnMessageReceived(const IPC::Message& message); |
+ |
+ protected: |
static const int kSize = 1024; |
+ static const int kMaxWait = 500; |
- AudioRendererImplTest() { |
- message_loop_.reset(new MessageLoop(MessageLoop::TYPE_IO)); |
+ AudioRendererImplTest() { } |
+ virtual ~AudioRendererImplTest() { } |
- // TODO(scherkus): use gmock with AudioMessageFilter to verify |
- // AudioRendererImpl calls or doesn't call Send(). |
- filter_ = new AudioMessageFilter(kRouteId); |
- filter_->message_loop_ = message_loop_.get(); |
+ // Test fixture's SetUp. |
+ virtual void SetUp() { |
+ // This part sets up a RenderThread environment to ensure that |
+ // RenderThread::current() (<=> TLS pointer) is valid. |
+ // Main parts are inspired by the RenderViewFakeResourcesTest. |
+ // Note that, the IPC part is not utilized in this test. |
+ content::GetContentClient()->set_renderer(&chrome_content_renderer_client_); |
+ static const char kThreadName[] = "RenderThread"; |
+ channel_.reset(new IPC::Channel(kThreadName, |
+ IPC::Channel::MODE_SERVER, this)); |
+ ASSERT_TRUE(channel_->Connect()); |
+ |
+ mock_process_.reset(new MockRenderProcess); |
+ render_thread_ = new RenderThread(kThreadName); |
+ mock_process_->set_main_thread(render_thread_); |
+ |
// Create temporary shared memory. |
CHECK(shared_mem_.CreateAnonymous(kSize)); |
@@ -33,84 +105,152 @@ |
decoder_ = new media::MockAudioDecoder(); |
ON_CALL(*decoder_, config()) |
- .WillByDefault(Return(media::AudioDecoderConfig(16, |
- CHANNEL_LAYOUT_MONO, |
- 44100))); |
+ .WillByDefault(Return(media::AudioDecoderConfig(16, |
+ CHANNEL_LAYOUT_MONO, |
+ 44100))); |
- // Create and initialize audio renderer. |
- renderer_ = new AudioRendererImpl(filter_); |
+ // Create and initialize the audio renderer. |
+ renderer_ = new AudioRendererImpl(); |
renderer_->set_host(&host_); |
renderer_->Initialize(decoder_, media::NewExpectedCallback()); |
- // Run pending tasks and simulate responding with a created audio stream. |
- message_loop_->RunAllPending(); |
+ // Wraps delegate calls into tasks. |
+ delegate_caller_ = new DelegateCaller(renderer_); |
+ // We need an event to verify that all tasks are done before leaving |
+ // our tests. |
+ event_.reset(new base::WaitableEvent(false, false)); |
+ |
// Duplicate the shared memory handle so both the test and the callee can |
// close their copy. |
base::SharedMemoryHandle duplicated_handle; |
EXPECT_TRUE(shared_mem_.ShareToProcess(base::GetCurrentProcessHandle(), |
- &duplicated_handle)); |
+ &duplicated_handle)); |
- renderer_->OnCreated(duplicated_handle, kSize); |
+ // Set things up and ensure that the call comes from the IO thread |
+ // as all AudioMessageFilter::Delegate methods. Don't have to wait |
+ // here. |
+ ChildProcess::current()->io_message_loop()->PostTask( |
+ FROM_HERE, |
+ NewRunnableMethod(delegate_caller_.get(), |
+ &DelegateCaller::OnCreated, duplicated_handle, kSize)); |
} |
- virtual ~AudioRendererImplTest() { |
+ // Test fixture's TearDown. |
+ virtual void TearDown() { |
+ mock_process_.reset(); |
} |
- protected: |
- // Fixtures. |
- scoped_ptr<MessageLoop> message_loop_; |
- scoped_refptr<AudioMessageFilter> filter_; |
+ // Fixture members |
+ MessageLoopForIO message_loop_; |
+ chrome::ChromeContentRendererClient chrome_content_renderer_client_; |
+ scoped_ptr<IPC::Channel> channel_; |
+ RenderThread* render_thread_; // owned by mock_process_ |
+ scoped_ptr<MockRenderProcess> mock_process_; |
base::SharedMemory shared_mem_; |
media::MockFilterHost host_; |
scoped_refptr<media::MockAudioDecoder> decoder_; |
scoped_refptr<AudioRendererImpl> renderer_; |
+ scoped_ptr<base::WaitableEvent> event_; |
+ scoped_refptr<DelegateCaller> delegate_caller_; |
private: |
DISALLOW_COPY_AND_ASSIGN(AudioRendererImplTest); |
}; |
+bool AudioRendererImplTest::OnMessageReceived( |
+ const IPC::Message& message) { |
tommi (sloooow) - chröme
2011/06/30 20:24:44
fix indent in this function.
henrika_dont_use
2011/07/01 08:41:25
Done.
|
+ NOTIMPLEMENTED(); |
+ return true; |
+} |
+ |
TEST_F(AudioRendererImplTest, SetPlaybackRate) { |
- // Execute SetPlaybackRate() codepath to create an IPC message. |
- |
- // Toggle play/pause to generate some IPC messages. |
+ // Execute SetPlaybackRate() codepath by toggling play/pause. |
+ // These methods will be called on the pipeline thread but calling from |
+ // here is fine for this test. Tasks will be posted internally on |
+ // the IO thread. |
renderer_->SetPlaybackRate(0.0f); |
renderer_->SetPlaybackRate(1.0f); |
renderer_->SetPlaybackRate(0.0f); |
renderer_->Stop(media::NewExpectedCallback()); |
- message_loop_->RunAllPending(); |
+ |
+ // Post a final task to the IO message loop and wait for completion. |
+ // When the event is signaled, we know that all posted task in this |
+ // test are done. |
+ ChildProcess::current()->io_message_loop()->PostTask( |
+ FROM_HERE, new WaitTask(event_.get())); |
+ EXPECT_TRUE(event_->TimedWait(base::TimeDelta::FromMilliseconds(kMaxWait))); |
} |
TEST_F(AudioRendererImplTest, SetVolume) { |
- // Execute SetVolume() codepath to create an IPC message. |
+ // Execute SetVolume() codepath. |
+ // This method will be called on the pipeline thread IRL. |
+ // Tasks will be posted internally on the IO thread. |
renderer_->SetVolume(0.5f); |
+ |
renderer_->Stop(media::NewExpectedCallback()); |
- message_loop_->RunAllPending(); |
+ |
+ // Post a final task to the IO message loop and wait for completion. |
+ ChildProcess::current()->io_message_loop()->PostTask( |
+ FROM_HERE, new WaitTask(event_.get())); |
+ EXPECT_TRUE(event_->TimedWait(base::TimeDelta::FromMilliseconds(kMaxWait))); |
} |
TEST_F(AudioRendererImplTest, Stop) { |
- // Execute Stop() codepath to create an IPC message. |
+ // Execute Stop() codepath. |
+ // Tasks will be posted internally on the IO thread. |
renderer_->Stop(media::NewExpectedCallback()); |
- message_loop_->RunAllPending(); |
// Run AudioMessageFilter::Delegate methods, which can be executed after being |
- // stopped. AudioRendererImpl shouldn't create any messages. |
- renderer_->OnRequestPacket(AudioBuffersState(kSize, 0)); |
- renderer_->OnStateChanged(kAudioStreamError); |
- renderer_->OnStateChanged(kAudioStreamPlaying); |
- renderer_->OnStateChanged(kAudioStreamPaused); |
- renderer_->OnCreated(shared_mem_.handle(), kSize); |
- renderer_->OnVolume(0.5); |
+ // stopped. AudioRendererImpl shouldn't create any messages in this state. |
+ // All delegate method calls are posted on the IO thread since it is |
+ // a requirement. |
+ ChildProcess::current()->io_message_loop()->PostTask( |
+ FROM_HERE, |
+ NewRunnableMethod(delegate_caller_.get(), |
+ &DelegateCaller::OnRequestPacket, AudioBuffersState(kSize, 0))); |
+ ChildProcess::current()->io_message_loop()->PostTask( |
+ FROM_HERE, |
+ NewRunnableMethod(delegate_caller_.get(), |
+ &DelegateCaller::OnStateChanged, kAudioStreamError)); |
+ ChildProcess::current()->io_message_loop()->PostTask( |
+ FROM_HERE, |
+ NewRunnableMethod(delegate_caller_.get(), |
+ &DelegateCaller::OnStateChanged, kAudioStreamPlaying)); |
+ ChildProcess::current()->io_message_loop()->PostTask( |
+ FROM_HERE, |
+ NewRunnableMethod(delegate_caller_.get(), |
+ &DelegateCaller::OnStateChanged, kAudioStreamPaused)); |
+ ChildProcess::current()->io_message_loop()->PostTask( |
+ FROM_HERE, |
+ NewRunnableMethod(delegate_caller_.get(), |
+ &DelegateCaller::OnCreated, shared_mem_.handle(), kSize)); |
+ ChildProcess::current()->io_message_loop()->PostTask( |
+ FROM_HERE, |
+ NewRunnableMethod(delegate_caller_.get(), |
+ &DelegateCaller::OnVolume, 0.5)); |
+ // Post a final task to the IO message loop and wait for completion. |
+ ChildProcess::current()->io_message_loop()->PostTask( |
+ FROM_HERE, new WaitTask(event_.get())); |
+ EXPECT_TRUE(event_->TimedWait(base::TimeDelta::FromMilliseconds(kMaxWait))); |
+ |
// It's possible that the upstream decoder replies right after being stopped. |
scoped_refptr<media::Buffer> buffer(new media::DataBuffer(kSize)); |
renderer_->ConsumeAudioSamples(buffer); |
} |
TEST_F(AudioRendererImplTest, DestroyedMessageLoop_SetPlaybackRate) { |
- // Kill the message loop and verify SetPlaybackRate() still works. |
- message_loop_.reset(); |
+ // Emulate "killing the message loop" and verify that SetPlaybackRate() |
+ // still works. |
+ ChildProcess::current()->io_message_loop()->PostTask( |
+ FROM_HERE, |
+ NewRunnableMethod(delegate_caller_.get(), |
+ &DelegateCaller::DestroyCurrentMessageLoop)); |
+ |
+ // No tasks will be posted on the IO thread here since we are in |
+ // a "stopped" state. |
renderer_->SetPlaybackRate(0.0f); |
renderer_->SetPlaybackRate(1.0f); |
renderer_->SetPlaybackRate(0.0f); |
@@ -118,15 +258,29 @@ |
} |
TEST_F(AudioRendererImplTest, DestroyedMessageLoop_SetVolume) { |
- // Kill the message loop and verify SetVolume() still works. |
- message_loop_.reset(); |
+ // Emulate "killing the message loop" and verify that SetVolume() |
+ // still works. |
+ ChildProcess::current()->io_message_loop()->PostTask( |
+ FROM_HERE, |
+ NewRunnableMethod(delegate_caller_.get(), |
+ &DelegateCaller::DestroyCurrentMessageLoop)); |
+ |
+ // No tasks will be posted on the IO thread here since we are in |
+ // a "stopped" state. |
renderer_->SetVolume(0.5f); |
renderer_->Stop(media::NewExpectedCallback()); |
} |
TEST_F(AudioRendererImplTest, DestroyedMessageLoop_ConsumeAudioSamples) { |
- // Kill the message loop and verify OnReadComplete() still works. |
- message_loop_.reset(); |
+ // Emulate "killing the message loop" and verify thata OnReadComplete() |
+ // still works. |
+ ChildProcess::current()->io_message_loop()->PostTask( |
+ FROM_HERE, |
+ NewRunnableMethod(delegate_caller_.get(), |
+ &DelegateCaller::DestroyCurrentMessageLoop)); |
+ |
+ // No tasks will be posted on the IO thread here since we are in |
+ // a "stopped" state. |
scoped_refptr<media::Buffer> buffer(new media::DataBuffer(kSize)); |
renderer_->ConsumeAudioSamples(buffer); |
renderer_->Stop(media::NewExpectedCallback()); |