| Index: content/browser/speech/speech_recognition_audio_receiver_unittest.cc
|
| diff --git a/content/browser/speech/speech_recognition_audio_receiver_unittest.cc b/content/browser/speech/speech_recognition_audio_receiver_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..a0ea0f97bc22e1a89e0a426cb1ca4590103f78e8
|
| --- /dev/null
|
| +++ b/content/browser/speech/speech_recognition_audio_receiver_unittest.cc
|
| @@ -0,0 +1,168 @@
|
| +// 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/bind.h"
|
| +#include "base/memory/shared_memory.h"
|
| +#include "base/message_loop/message_loop.h"
|
| +#include "base/process/process_handle.h"
|
| +#include "base/test/test_timeouts.h"
|
| +#include "base/threading/thread.h"
|
| +#include "base/threading/thread_checker.h"
|
| +#include "base/threading/thread_restrictions.h"
|
| +#include "base/time/time.h"
|
| +#include "content/browser/browser_thread_impl.h"
|
| +#include "content/browser/speech/speech_recognition_audio_receiver.h"
|
| +#include "media/audio/audio_parameters.h"
|
| +#include "media/base/audio_bus.h"
|
| +#include "testing/gmock/include/gmock/gmock.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +using testing::_;
|
| +
|
| +namespace content {
|
| +
|
| +namespace {
|
| +
|
| +// Used to terminate a loop from a different thread than the loop belongs to.
|
| +// |loop| should be a MessageLoopProxy.
|
| +ACTION_P(QuitLoop, loop) {
|
| + loop->PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
|
| +}
|
| +
|
| +void SendIndexViaSocket(base::SyncSocket* socket, uint32 index) {
|
| + size_t bytes_sent = socket->Send(&index, sizeof(index));
|
| + EXPECT_EQ(bytes_sent, sizeof(index));
|
| +}
|
| +
|
| +class MockEventHandler : public media::AudioInputController::EventHandler {
|
| + public:
|
| + MockEventHandler() {}
|
| + virtual ~MockEventHandler() {}
|
| +
|
| + MOCK_METHOD1(OnCreated, void(media::AudioInputController* controller));
|
| + MOCK_METHOD1(OnRecording, void(media::AudioInputController* controller));
|
| + MOCK_METHOD2(OnLog, void(media::AudioInputController* controller,
|
| + const std::string& message));
|
| + MOCK_METHOD2(OnError, void(media::AudioInputController* controller,
|
| + media::AudioInputController::ErrorCode error));
|
| + MOCK_METHOD2(OnData, void(media::AudioInputController* controller,
|
| + const media::AudioBus* data));
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +class SpeechRecognitionAudioReceiverTest : public testing::Test {
|
| + public:
|
| + SpeechRecognitionAudioReceiverTest()
|
| + : io_thread_(BrowserThread::IO, &io_loop_),
|
| + audio_thread_("AudioReceiverThread") {
|
| + audio_thread_.Start();
|
| +
|
| + media::AudioParameters audio_params(
|
| + media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
|
| + media::CHANNEL_LAYOUT_STEREO,
|
| + 16000, 16, 1600);
|
| + base::SharedMemoryHandle foreign_memory_handle;
|
| + base::SyncSocket::TransitDescriptor foreign_socket_descriptor;
|
| + receiver_ = SpeechRecognitionAudioReceiver::Create(
|
| + audio_params, base::GetCurrentProcessHandle(), &event_handler_,
|
| + audio_thread_.task_runner().get(),
|
| + &foreign_memory_handle, &foreign_socket_descriptor);
|
| + DCHECK(receiver_.get());
|
| +
|
| + // Create a send socket.
|
| + send_socket_.reset(new base::CancelableSyncSocket(
|
| + base::SyncSocket::UnwrapHandle(foreign_socket_descriptor)));
|
| +
|
| + // Get the shared memory.
|
| + shared_memory_.reset(new base::SharedMemory(foreign_memory_handle, false));
|
| + const size_t memory_length =
|
| + media::AudioBus::CalculateMemorySize(audio_params) +
|
| + sizeof(media::AudioInputBufferParameters);
|
| + DCHECK(shared_memory_->Map(memory_length));
|
| +
|
| + // Peer's buffer index is accessed via |params.size| in shared memory.
|
| + uint8* ptr = static_cast<uint8*>(shared_memory_->memory());
|
| + media::AudioInputBuffer* buffer =
|
| + reinterpret_cast<media::AudioInputBuffer*>(ptr);
|
| + peer_buffer_index_ = &(buffer->params.size);
|
| +
|
| + // The peer must manage their own counter and reset it.
|
| + //ASSERT_EQ(0U, *peer_buffer_index_);
|
| + output_bus_ = media::AudioBus::WrapMemory(audio_params, buffer->audio);
|
| +
|
| + // Start the receiver.
|
| + EXPECT_CALL(event_handler_, OnRecording(NULL))
|
| + .WillOnce(QuitLoop(io_loop()));
|
| + receiver_->StartListening();
|
| + WaitUntilCallback();
|
| + }
|
| +
|
| + virtual ~SpeechRecognitionAudioReceiverTest() {
|
| + }
|
| +
|
| + protected:
|
| + // Each test case needs to Stop() before quiting.
|
| + void Stop() {
|
| + ASSERT_EQ(io_loop(), base::MessageLoop::current());
|
| + receiver_->Stop();
|
| + const bool previous_io_allowed =
|
| + base::ThreadRestrictions::SetIOAllowed(true);
|
| + audio_thread_.Stop();
|
| + base::ThreadRestrictions::SetIOAllowed(previous_io_allowed);
|
| + }
|
| +
|
| + void WaitUntilCallback() {
|
| + // Don't hang the test if we never get the Render() callback.
|
| + io_loop()->PostDelayedTask(FROM_HERE, base::MessageLoop::QuitClosure(),
|
| + TestTimeouts::action_timeout());
|
| + io_loop()->Run();
|
| + }
|
| +
|
| + base::MessageLoopForIO* io_loop() { return &io_loop_; }
|
| + base::MessageLoop* audio_loop() { return audio_thread_.message_loop(); }
|
| +
|
| + base::MessageLoopForIO io_loop_;
|
| + BrowserThreadImpl io_thread_;
|
| + MockEventHandler event_handler_;
|
| + scoped_ptr<base::CancelableSyncSocket> send_socket_;
|
| +
|
| + private:
|
| + uint32 peer_buffer_index() { return *peer_buffer_index_; }
|
| +
|
| + base::Thread audio_thread_;
|
| + scoped_ptr<SpeechRecognitionAudioReceiver> receiver_;
|
| + scoped_ptr<base::SharedMemory> shared_memory_;
|
| + scoped_ptr<media::AudioBus> output_bus_;
|
| + uint32* peer_buffer_index_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(SpeechRecognitionAudioReceiverTest);
|
| +};
|
| +
|
| +TEST_F(SpeechRecognitionAudioReceiverTest, CreateAndStop) {
|
| + // Objects are created, initialized, and stopped.
|
| + Stop();
|
| +};
|
| +
|
| +TEST_F(SpeechRecognitionAudioReceiverTest, VerifyDataFlow) {
|
| + int index = 1;
|
| + EXPECT_CALL(event_handler_, OnData(NULL, _))
|
| + .WillOnce(QuitLoop(io_loop()));
|
| + SendIndexViaSocket(send_socket_.get(), index);
|
| + WaitUntilCallback();
|
| + Stop();
|
| +};
|
| +
|
| +TEST_F(SpeechRecognitionAudioReceiverTest, SocketClosingTriggersError) {
|
| + EXPECT_CALL(event_handler_, OnData(NULL, _))
|
| + .Times(testing::AtMost(1));
|
| + EXPECT_CALL(event_handler_, OnError(NULL, _))
|
| + .WillOnce(QuitLoop(io_loop()));
|
| + SendIndexViaSocket(send_socket_.get(), 0U);
|
| + send_socket_->Shutdown();
|
| + WaitUntilCallback();
|
| + Stop();
|
| +};
|
| +
|
| +} // namespace content
|
|
|