summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/renderer_host/audio_renderer_host.cc3
-rw-r--r--chrome/browser/renderer_host/audio_renderer_host.h2
-rw-r--r--chrome/browser/renderer_host/audio_renderer_host_unittest.cc206
3 files changed, 204 insertions, 7 deletions
diff --git a/chrome/browser/renderer_host/audio_renderer_host.cc b/chrome/browser/renderer_host/audio_renderer_host.cc
index 4550c1f..4ba0a24 100644
--- a/chrome/browser/renderer_host/audio_renderer_host.cc
+++ b/chrome/browser/renderer_host/audio_renderer_host.cc
@@ -75,7 +75,8 @@ AudioRendererHost::IPCAudioSource::IPCAudioSource(
}
AudioRendererHost::IPCAudioSource::~IPCAudioSource() {
- DCHECK_EQ(AudioOutputStream::STATE_STOPPED, state_);
+ DCHECK(AudioOutputStream::STATE_STOPPED == state_ ||
+ AudioOutputStream::STATE_CREATED == state_);
}
// static
diff --git a/chrome/browser/renderer_host/audio_renderer_host.h b/chrome/browser/renderer_host/audio_renderer_host.h
index c4be5a7..01a3545 100644
--- a/chrome/browser/renderer_host/audio_renderer_host.h
+++ b/chrome/browser/renderer_host/audio_renderer_host.h
@@ -93,7 +93,7 @@ class AudioRendererHost : public base::RefCountedThreadSafe<AudioRendererHost> {
// Destruction can happen on either UI thread or IO thread, but at destruction
// all associated sources are destroyed and streams are closed.
- ~AudioRendererHost();
+ virtual ~AudioRendererHost();
// Called from UI thread from the owner of this object to kick start
// destruction of streams in IO thread.
diff --git a/chrome/browser/renderer_host/audio_renderer_host_unittest.cc b/chrome/browser/renderer_host/audio_renderer_host_unittest.cc
index 8efa3e34..62be795 100644
--- a/chrome/browser/renderer_host/audio_renderer_host_unittest.cc
+++ b/chrome/browser/renderer_host/audio_renderer_host_unittest.cc
@@ -3,31 +3,227 @@
// found in the LICENSE file.
#include "base/message_loop.h"
-#include "base/process.h"
+#include "base/process_util.h"
#include "base/scoped_ptr.h"
#include "chrome/browser/renderer_host/audio_renderer_host.h"
+#include "chrome/common/render_messages.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Return;
+using ::testing::SetArgumentPointee;
+
+namespace {
+
+const int kInvalidId = -1;
+const int kProcessId = 100;
+const int kRouteId = 200;
+const int kBufferCapacity = 65536;
+const int kPacketSize = 16384;
+
+} // namespace
+
+class MockAudioRendererHost : public AudioRendererHost {
+ public:
+ MockAudioRendererHost(MessageLoop* loop)
+ : AudioRendererHost(loop) {
+ }
+
+ virtual ~MockAudioRendererHost() {
+ }
+
+ // A list of mock methods.
+ MOCK_METHOD4(OnRequestPacket,
+ void(int routing_id, int stream_id,
+ size_t bytes_in_buffer, int64 message_timestamp));
+
+ MOCK_METHOD3(OnStreamCreated,
+ void(int routing_id, int stream_id, int length));
+
+ MOCK_METHOD4(OnStreamStateChanged,
+ void(int routing_id, int stream_id,
+ AudioOutputStream::State state, int info));
+
+ MOCK_METHOD4(OnStreamVolume,
+ void(int routing_id, int stream_id, double left, double right));
+
+ base::SharedMemory* shared_memory() { return shared_memory_.get(); }
+
+ protected:
+ // This method is used to dispatch IPC messages to the renderer. We intercept
+ // these messages here and dispatch to our mock methods to verify the
+ // conversation between this object and the renderer.
+ virtual void Send(IPC::Message* message) {
+ CHECK(message);
+
+ // In this method we dispatch the messages to the according handlers as if
+ // we are the renderer.
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(MockAudioRendererHost, *message)
+ IPC_MESSAGE_HANDLER(ViewMsg_RequestAudioPacket, OnRequestPacket)
+ IPC_MESSAGE_HANDLER(ViewMsg_NotifyAudioStreamCreated, OnStreamCreated)
+ IPC_MESSAGE_HANDLER(ViewMsg_NotifyAudioStreamStateChanged,
+ OnStreamStateChanged)
+ IPC_MESSAGE_HANDLER(ViewMsg_NotifyAudioStreamVolume, OnStreamVolume)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ EXPECT_TRUE(handled);
+
+ delete message;
+ }
+
+ private:
+ // These handler methods do minimal things and delegate to the mock methods.
+ void OnRequestPacket(const IPC::Message& msg, int stream_id,
+ size_t bytes_in_buffer, int64 message_timestamp) {
+ OnRequestPacket(msg.routing_id(), stream_id, bytes_in_buffer,
+ message_timestamp);
+ }
+
+ void OnStreamCreated(const IPC::Message& msg, int stream_id,
+ base::SharedMemoryHandle handle, int length) {
+ // Maps the shared memory.
+ shared_memory_.reset(new base::SharedMemory(handle, true));
+ CHECK(shared_memory_->Map(length));
+ CHECK(shared_memory_->memory());
+
+ // And then delegate the call to the mock method.
+ OnStreamCreated(msg.routing_id(), stream_id, length);
+ }
+
+ void OnStreamStateChanged(const IPC::Message& msg, int stream_id,
+ AudioOutputStream::State state, int info) {
+ OnStreamStateChanged(msg.routing_id(), stream_id, state, info);
+ }
+
+ void OnStreamVolume(const IPC::Message& msg, int stream_id,
+ double left, double right) {
+ OnStreamVolume(msg.routing_id(), stream_id, left, right);
+ }
+
+ scoped_ptr<base::SharedMemory> shared_memory_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockAudioRendererHost);
+};
+
class AudioRendererHostTest : public testing::Test {
+ public:
+ AudioRendererHostTest()
+ : current_stream_id_(0) {
+ }
+
protected:
virtual void SetUp() {
// Create a message loop so AudioRendererHost can use it.
message_loop_.reset(new MessageLoop(MessageLoop::TYPE_IO));
- host_ = new AudioRendererHost(message_loop_.get());
+ host_ = new MockAudioRendererHost(message_loop_.get());
}
virtual void TearDown() {
// This task post a task to message_loop_ to do internal destruction on
// message_loop_.
host_->Destroy();
+
// We need to continue running message_loop_ to complete all destructions.
message_loop_->RunAllPending();
+
+ // Release the reference to the mock object.
+ host_ = NULL;
+ }
+
+ AudioRendererHost::IPCAudioSource* CreateAudioStream(
+ AudioManager::Format format) {
+ InSequence s;
+
+ // 1. We will first receive a OnStreamCreated() signal.
+ EXPECT_CALL(*host_,
+ OnStreamCreated(kRouteId, current_stream_id_, kPacketSize));
+
+ // 2. First packet request will arrive. This request is sent by
+ // IPCAudioSource::CreateIPCAudioSource to start buffering.
+ EXPECT_CALL(*host_, OnRequestPacket(kRouteId, current_stream_id_, 0, _));
+
+ AudioRendererHost::IPCAudioSource* source =
+ AudioRendererHost::IPCAudioSource::CreateIPCAudioSource(
+ host_,
+ kProcessId,
+ kRouteId,
+ current_stream_id_,
+ base::GetCurrentProcessHandle(),
+ format,
+ 2,
+ AudioManager::kAudioCDSampleRate,
+ 16,
+ kPacketSize,
+ kBufferCapacity);
+ EXPECT_TRUE(source);
+ EXPECT_EQ(kProcessId, source->process_id());
+ EXPECT_EQ(kRouteId, source->route_id());
+ EXPECT_EQ(current_stream_id_, source->stream_id());
+ return source;
+ }
+
+ AudioRendererHost::IPCAudioSource* CreateRealStream() {
+ return CreateAudioStream(AudioManager::AUDIO_PCM_LINEAR);
+ }
+
+ AudioRendererHost::IPCAudioSource* CreateMockStream() {
+ return CreateAudioStream(AudioManager::AUDIO_MOCK);
}
- scoped_refptr<AudioRendererHost> host_;
+ int current_stream_id_;
+ scoped_refptr<MockAudioRendererHost> host_;
scoped_ptr<MessageLoop> message_loop_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AudioRendererHostTest);
};
-TEST_F(AudioRendererHostTest, NoTest) {
- // TODO(hclam): come up with useful tests.
+// Audio output stream only works stably on windows. Also there's no mock
+// audio output streams for mac and linux.
+// TODO(hclam): make these tests work on mac and linux.
+#if defined(OS_WIN)
+
+TEST_F(AudioRendererHostTest, MockStreamDataConversation) {
+ scoped_ptr<AudioRendererHost::IPCAudioSource> source(CreateMockStream());
+
+ // We will receive packet requests until the buffer is full. We first send
+ // three packets of 16KB, then we send packets of 1KB until the buffer is
+ // full. Then there will no more packet requests.
+ InSequence s;
+ EXPECT_CALL(*host_,
+ OnRequestPacket(kRouteId, current_stream_id_, kPacketSize, _));
+ EXPECT_CALL(*host_,
+ OnRequestPacket(kRouteId, current_stream_id_, 2 * kPacketSize, _));
+
+ const int kStep = kPacketSize / 4;
+ EXPECT_CALL(*host_,
+ OnRequestPacket(kRouteId, current_stream_id_,
+ 2 * kPacketSize + kStep, _));
+ EXPECT_CALL(*host_,
+ OnRequestPacket(kRouteId, current_stream_id_,
+ 2 * kPacketSize + 2 * kStep, _));
+ EXPECT_CALL(*host_,
+ OnRequestPacket(kRouteId, current_stream_id_,
+ 2 * kPacketSize + 3 * kStep, _));
+ EXPECT_CALL(*host_,
+ OnRequestPacket(kRouteId, current_stream_id_, 3 * kPacketSize, _));
+ EXPECT_CALL(*host_, OnStreamStateChanged(kRouteId,
+ current_stream_id_,
+ AudioOutputStream::STATE_STARTED,
+ 0));
+
+ source->NotifyPacketReady(kPacketSize);
+ source->NotifyPacketReady(kPacketSize);
+ source->NotifyPacketReady(kStep);
+ source->NotifyPacketReady(kStep);
+ source->NotifyPacketReady(kStep);
+ source->NotifyPacketReady(kStep);
+ source->Start();
+ source->Close();
}
+
+#endif