Chromium Code Reviews| Index: media/audio/audio_input_device_unittest.cc |
| diff --git a/media/audio/audio_input_device_unittest.cc b/media/audio/audio_input_device_unittest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..bebc1a18c742e1324442e800cd2c1ca530c0aaf0 |
| --- /dev/null |
| +++ b/media/audio/audio_input_device_unittest.cc |
| @@ -0,0 +1,215 @@ |
| +// Copyright 2014 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "base/memory/shared_memory.h" |
| +#include "base/message_loop/message_loop.h" |
| +#include "base/process/process_handle.h" |
| +#include "base/sync_socket.h" |
| +#include "base/test/test_timeouts.h" |
| +#include "media/audio/audio_device_thread.h" |
| +#include "media/audio/audio_input_device.h" |
| +#include "media/audio/audio_input_ipc.h" |
| +#include "media/base/audio_capturer_source.h" |
| +#include "testing/gmock/include/gmock/gmock.h" |
| +#include "testing/gtest/include/gtest/gtest.h" |
| + |
| +using base::CancelableSyncSocket; |
| +using base::SharedMemory; |
| +using base::SyncSocket; |
| +using testing::_; |
| +using testing::StrictMock; |
| + |
| +namespace media { |
| + |
| +namespace { |
| + |
| +// The number of shared memory buffers used by AudioInputDevice. |
| +static const int kNumOfSharedMemoriesInAID = 10; |
| + |
| +int CalculateMemorySize(const AudioParameters& params) { |
| + return sizeof(AudioInputBufferParameters) + |
| + AudioBus::CalculateMemorySize(params); |
| +} |
| + |
| +// Creates a copy of a SyncSocket handle that we can give to AudioInputDevice. |
| +// On Windows this means duplicating the pipe handle so that AudioInputDevice |
| +// can call CloseHandle() (since ownership has been transferred), but on other |
| +// platforms, we just copy the same socket handle since AudioInputDevice on |
| +// those platforms won't actually own the socket (FileDescriptor.auto_close is |
| +// false). |
| +bool DuplicateSocketHandle(SyncSocket::Handle socket_handle, |
| + SyncSocket::Handle* copy) { |
| +#if defined(OS_WIN) |
| + HANDLE process = GetCurrentProcess(); |
| + ::DuplicateHandle(process, socket_handle, process, copy, |
| + 0, FALSE, DUPLICATE_SAME_ACCESS); |
| + return *copy != NULL; |
| +#else |
| + *copy = socket_handle; |
| + return *copy != -1; |
| +#endif |
| +} |
| + |
| +class MockCaptureCallback : public AudioCapturerSource::CaptureCallback { |
| + public: |
| + MockCaptureCallback() {} |
| + virtual ~MockCaptureCallback() {} |
| + |
| + MOCK_METHOD4(Capture, void(const AudioBus* audio_source, |
| + int audio_delay_milliseconds, |
| + double volume, |
| + bool key_pressed)); |
| + MOCK_METHOD0(OnCaptureError, void()); |
| +}; |
| + |
| +class MockAudioInputIPC : public AudioInputIPC { |
| + public: |
| + MockAudioInputIPC() {} |
| + virtual ~MockAudioInputIPC() {} |
| + |
| + MOCK_METHOD5(CreateStream, void(AudioInputIPCDelegate* delegate, |
| + int session_id, |
| + const AudioParameters& params, |
| + bool automatic_gain_control, |
| + uint32 total_segments)); |
| + MOCK_METHOD0(RecordStream, void()); |
| + MOCK_METHOD1(SetVolume, void(double volume)); |
| + MOCK_METHOD0(CloseStream, void()); |
| +}; |
| + |
| +// Used to terminate a loop from a different thread than the loop belongs to. |
| +// |loop| should be a SingleThreadTaskRunner. |
| +ACTION_P(QuitLoop, task_runner) { |
| + task_runner->PostTask(FROM_HERE, base::MessageLoop::QuitClosure()); |
| +} |
| + |
| +} // namespace. |
| + |
| +class AudioInputDeviceTest : public testing::Test { |
| + protected: |
| + AudioInputDeviceTest() |
| + : audio_parameters_(AudioParameters::AUDIO_PCM_LOW_LATENCY, |
| + CHANNEL_LAYOUT_STEREO, |
| + 44100, |
| + 16, |
| + 441), |
|
tommi (sloooow) - chröme
2014/09/02 12:03:34
Did you get a chance to run tests with very small
|
| + io_loop_(new base::MessageLoopForIO), |
| + mock_ipc_(new MockAudioInputIPC()), |
| + audio_input_device_( |
| + new AudioInputDevice(scoped_ptr<AudioInputIPC>(mock_ipc_), |
| + io_loop_->message_loop_proxy())) { |
| + audio_input_device_->Initialize(audio_parameters_, &callback_, 1); |
| + io_loop_->RunUntilIdle(); |
| + } |
| + |
| + virtual ~AudioInputDeviceTest() {} |
| + |
| + void StartAudioInputDevice() { |
| + audio_input_device_->Start(); |
| + EXPECT_CALL( |
| + *mock_ipc_, |
| + CreateStream( |
| + audio_input_device_.get(), 1, _, false, kNumOfSharedMemoriesInAID)); |
| + io_loop_->RunUntilIdle(); |
| + } |
| + |
| + void CreateInputStream() { |
| + const int kMemorySize = |
| + CalculateMemorySize(audio_parameters_) * kNumOfSharedMemoriesInAID; |
| + |
| + ASSERT_TRUE(shared_memory_.CreateAndMapAnonymous(kMemorySize)); |
| + memset(shared_memory_.memory(), 0xff, kMemorySize); |
| + |
| + ASSERT_TRUE( |
| + CancelableSyncSocket::CreatePair(&browser_socket_, &renderer_socket_)); |
| + |
| + SyncSocket::Handle socket = SyncSocket::kInvalidHandle; |
| + ASSERT_TRUE(DuplicateSocketHandle(renderer_socket_.handle(), |
| + &socket)); |
| + base::SharedMemoryHandle duplicated_memory_handle; |
| + ASSERT_TRUE(shared_memory_.ShareToProcess(base::GetCurrentProcessHandle(), |
| + &duplicated_memory_handle)); |
| + |
| + EXPECT_CALL(*mock_ipc_, RecordStream()); |
| + audio_input_device_->OnStreamCreated(duplicated_memory_handle, |
| + socket, |
| + shared_memory_.requested_size(), |
| + kNumOfSharedMemoriesInAID); |
| + io_loop_->RunUntilIdle(); |
| + } |
| + |
| + void WriteToSharedMemory() { |
| + const size_t kSegmentSize = CalculateMemorySize(audio_parameters_); |
| + uint8* ptr = static_cast<uint8*>(shared_memory_.memory()); |
| + for (int i = 0; i < kNumOfSharedMemoriesInAID; ++i) { |
| + AudioInputBuffer* buffer = reinterpret_cast<AudioInputBuffer*>(ptr); |
| + buffer->params.volume = 0; |
| + buffer->params.size = kSegmentSize; |
| + buffer->params.key_pressed = false; |
| + ptr += kSegmentSize; |
| + } |
| + } |
| + |
| + void ConsumeAndVerifySharedMemory() { |
| + const size_t kSegmentSize = CalculateMemorySize(audio_parameters_); |
| + uint8* ptr = static_cast<uint8*>(shared_memory_.memory()); |
| + EXPECT_CALL(callback_, Capture(_, _, _, _)) |
| + .WillRepeatedly(QuitLoop(io_loop_->task_runner())); |
| + for (int i = 0; i < kNumOfSharedMemoriesInAID; ++i) { |
| + AudioInputBuffer* buffer = reinterpret_cast<AudioInputBuffer*>(ptr); |
| + EXPECT_EQ(kSegmentSize, buffer->params.size); |
| + |
| + // Send a socket from the browser to trigger processing the shared memory |
| + // in AudioInputDevice. |
| + int pending_bytes = 0; |
| + browser_socket_.Send(&pending_bytes, sizeof(pending_bytes)); |
| + WaitForCaptureCallback(); |
| + |
| + // Verify the |size| of the buffer has been reset to 0; |
| + EXPECT_EQ(0u, buffer->params.size); |
| + |
| + // Iterate the next segment of shared memory. |
| + ptr += kSegmentSize; |
| + } |
| + } |
| + |
| + void StopAudioInputDevice() { |
| + audio_input_device_->Stop(); |
| + EXPECT_CALL(*mock_ipc_, CloseStream()); |
| + io_loop_->RunUntilIdle(); |
| + } |
| + |
| + void WaitForCaptureCallback() { |
| + io_loop_->PostDelayedTask(FROM_HERE, |
| + base::MessageLoop::QuitClosure(), |
| + TestTimeouts::action_timeout()); |
| + io_loop_->Run(); |
| + } |
| + |
| + protected: |
| + AudioParameters audio_parameters_; |
| + scoped_ptr<base::MessageLoopForIO> io_loop_; |
| + MockAudioInputIPC* mock_ipc_; // owned by |audio_input_device_|. |
| + scoped_refptr<AudioInputDevice> audio_input_device_; |
| + StrictMock<MockCaptureCallback> callback_; |
| + |
| + private: |
| + SharedMemory shared_memory_; |
| + CancelableSyncSocket browser_socket_; |
| + CancelableSyncSocket renderer_socket_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(AudioInputDeviceTest); |
| +}; |
| + |
| +// Sets up shared memory and AudioInputDevice, and verify the shared memory |
| +// is correctly written and consumed. |
| +TEST_F(AudioInputDeviceTest, VerifySharedMemory) { |
| + StartAudioInputDevice(); |
| + CreateInputStream(); |
| + WriteToSharedMemory(); |
| + ConsumeAndVerifySharedMemory(); |
| + StopAudioInputDevice(); |
| +} |
| + |
| +} // namespace media. |