| OLD | NEW |
| (Empty) | |
| 1 // Copyright 2014 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/memory/shared_memory.h" |
| 7 #include "base/message_loop/message_loop.h" |
| 8 #include "base/process/process_handle.h" |
| 9 #include "base/time/time.h" |
| 10 #include "content/browser/browser_thread_impl.h" |
| 11 #include "content/browser/speech/speech_recognition_audio_receiver.h" |
| 12 #include "media/audio/audio_parameters.h" |
| 13 #include "media/base/audio_bus.h" |
| 14 #include "testing/gmock/include/gmock/gmock.h" |
| 15 #include "testing/gtest/include/gtest/gtest.h" |
| 16 |
| 17 using testing::_; |
| 18 |
| 19 namespace { |
| 20 |
| 21 // Output audio format. |
| 22 const media::AudioParameters::Format kOutputFormat = |
| 23 media::AudioParameters::AUDIO_PCM_LOW_LATENCY; |
| 24 const media::ChannelLayout kOutputChannelLayout = media::CHANNEL_LAYOUT_STEREO; |
| 25 const int kOutputChannels = 2; |
| 26 const int kOutputSampleRate = 16000; |
| 27 const int kOutputBitsPerSample = 16; |
| 28 const int kOutputFramesPerBuffer = 1600; |
| 29 |
| 30 // Timeout for socket. |
| 31 const uint32 kMessages = 5; |
| 32 |
| 33 // Used for simulating behavior of the peer after the last socket receive was |
| 34 // triggered. The test scenario consists of a decision whether to close the |
| 35 // the socket, destroy the receiver and if there should be an error event |
| 36 // triggered in that scenario. |
| 37 enum TestScenario { |
| 38 // Used for negating in a bitwise and operation. E.g.: NOT & CLOSE_SOCKET. |
| 39 // Useful for readability. |
| 40 NOT = 0, |
| 41 |
| 42 // The socket is closed after last message is received. |
| 43 // The |Close()| occurs before the receiver gets destroyed. |
| 44 CLOSE_SOCKET = 1, |
| 45 |
| 46 // The receiver is destroyed right after last message is received |
| 47 // but not before the socket was closed if that's part of the scenario. |
| 48 DESTROY_RECEIVER = 2, |
| 49 |
| 50 // Indicate the chosen test scenario should expect an error callback or not. |
| 51 EXPECT_ERROR = 4 |
| 52 }; |
| 53 |
| 54 } // namespace |
| 55 |
| 56 namespace content { |
| 57 |
| 58 class SpeechRecognitionAudioReceiverTestBase |
| 59 : public testing::Test, |
| 60 public media::AudioInputController::EventHandler { |
| 61 public: |
| 62 SpeechRecognitionAudioReceiverTestBase() : |
| 63 io_thread_(BrowserThread::IO, &message_loop_), |
| 64 num_messages_(0), |
| 65 buffer_index_(0) {} |
| 66 |
| 67 virtual ~SpeechRecognitionAudioReceiverTestBase() {} |
| 68 |
| 69 void Initialize() { |
| 70 media::AudioParameters audio_params; |
| 71 audio_params.Reset(kOutputFormat, |
| 72 kOutputChannelLayout, |
| 73 kOutputChannels, |
| 74 kOutputSampleRate, |
| 75 kOutputBitsPerSample, |
| 76 kOutputFramesPerBuffer); |
| 77 |
| 78 base::SharedMemoryHandle foreign_memory_handle; |
| 79 base::SyncSocket::TransitDescriptor foreign_socket_descriptor; |
| 80 receiver_ = SpeechRecognitionAudioReceiver::Create( |
| 81 audio_params, |
| 82 base::GetCurrentProcessHandle(), |
| 83 this, |
| 84 base::MessageLoop::current()->task_runner().get(), |
| 85 &foreign_memory_handle, |
| 86 &foreign_socket_descriptor); |
| 87 ASSERT_TRUE(receiver_.get() != NULL); |
| 88 |
| 89 // Get the producer socket. |
| 90 producer_socket_.reset(new base::SyncSocket( |
| 91 base::SyncSocket::UnwrapHandle(foreign_socket_descriptor))); |
| 92 |
| 93 shared_memory_.reset(new base::SharedMemory(foreign_memory_handle, false)); |
| 94 |
| 95 // Get the shared memory. |
| 96 const size_t memory_length = |
| 97 media::AudioBus::CalculateMemorySize(audio_params) + |
| 98 sizeof(media::AudioInputBufferParameters); |
| 99 ASSERT_TRUE(shared_memory_->Map(memory_length)); |
| 100 |
| 101 // Peer's buffer index is accessed via |params.size| in shared memory. |
| 102 uint8* ptr = static_cast<uint8*>(shared_memory_->memory()); |
| 103 media::AudioInputBuffer* buffer = |
| 104 reinterpret_cast<media::AudioInputBuffer*>(ptr); |
| 105 peer_buffer_index_ = &(buffer->params.size); |
| 106 |
| 107 // Local counter of sent messages. |
| 108 buffer_index_ = 0; |
| 109 |
| 110 // The peer must manage their own counter and reset it. |
| 111 ASSERT_EQ(0U, *peer_buffer_index_); |
| 112 output_bus_ = media::AudioBus::WrapMemory(audio_params, buffer->audio); |
| 113 } |
| 114 |
| 115 protected: |
| 116 // Mock methods triggered by events and used for counting callbacks. |
| 117 MOCK_METHOD0(SendCalled, void()); |
| 118 MOCK_METHOD0(OnDataCalled, void()); |
| 119 MOCK_METHOD0(OnErrorCalled, void()); |
| 120 |
| 121 // Create the receiver and starts posting tasks to send |num_messages|. |
| 122 void SendMessages(const uint32 num_messages) { |
| 123 buffer_index_ = 0; |
| 124 num_messages_ = num_messages; |
| 125 |
| 126 Initialize(); |
| 127 |
| 128 // Start the sending soon on this thread. |
| 129 PostSendTask(); |
| 130 |
| 131 // We start listening after the send task was posted to catch the first |
| 132 // message in the same thread message loop. |
| 133 receiver_->Listen(); |
| 134 } |
| 135 |
| 136 base::SyncSocket* producer_socket() { return producer_socket_.get(); } |
| 137 uint32 peer_buffer_index() { return *peer_buffer_index_; } |
| 138 base::MessageLoopForIO * message_loop() { return &message_loop_; } |
| 139 |
| 140 void set_scenario(const TestScenario scenario) { scenario_ = scenario; } |
| 141 const TestScenario& scenario() const { return scenario_; } |
| 142 |
| 143 base::MessageLoopForIO message_loop_; |
| 144 BrowserThreadImpl io_thread_; |
| 145 |
| 146 private: |
| 147 // AudioInputController::EventHandler methods. |
| 148 void OnCreated(media::AudioInputController* controller) override {} |
| 149 void OnRecording(media::AudioInputController* controller) override {} |
| 150 void OnLog(media::AudioInputController* controller, |
| 151 const std::string& message) override {} |
| 152 |
| 153 void OnError( |
| 154 media::AudioInputController* controller, |
| 155 media::AudioInputController::ErrorCode error_code) override { |
| 156 // Trigger the mock callback for counting. |
| 157 OnErrorCalled(); |
| 158 } |
| 159 |
| 160 void OnData(media::AudioInputController* controller, |
| 161 const media::AudioBus* data) { |
| 162 // Check if we're getting the same data as sent. |
| 163 CheckAudioBus(data); |
| 164 |
| 165 if (buffer_index_ < num_messages_) { |
| 166 // Schedule another send once this receive completes. |
| 167 // The Send task should be scheduled before the Receive task. |
| 168 PostSendTask(); |
| 169 } else { |
| 170 // This path indicates we have received the last message. |
| 171 // Here we apply the current test scenario. |
| 172 if (scenario_ & CLOSE_SOCKET) { |
| 173 producer_socket()->Close(); |
| 174 } |
| 175 |
| 176 if (scenario_ & DESTROY_RECEIVER) { |
| 177 PostDestroyReceiverTask(); |
| 178 } |
| 179 } |
| 180 |
| 181 // Trigger the mock callback for counting. |
| 182 OnDataCalled(); |
| 183 } |
| 184 |
| 185 // Posts a task to trigger a send on the socket. |
| 186 void PostSendTask() { |
| 187 message_loop()->task_runner()->PostTask( |
| 188 FROM_HERE, |
| 189 base::Bind(&SpeechRecognitionAudioReceiverTestBase::Send, |
| 190 base::Unretained(this))); |
| 191 } |
| 192 |
| 193 // Sends a buffer to the receiver over the socket and check synchronization. |
| 194 void Send() { |
| 195 // Check buffer index synchronization is done. |
| 196 EXPECT_EQ(buffer_index_, peer_buffer_index()); |
| 197 |
| 198 // Render different sample data for each send. |
| 199 RenderSampleAudioToOutputBus(1 + static_cast<int16>(buffer_index_)); |
| 200 |
| 201 size_t sent_bytes = |
| 202 producer_socket()->Send(&buffer_index_, sizeof(buffer_index_)); |
| 203 EXPECT_EQ(sizeof(buffer_index_), sent_bytes); |
| 204 |
| 205 ++buffer_index_; |
| 206 |
| 207 // Trigger the mock callback for counting. |
| 208 SendCalled(); |
| 209 } |
| 210 |
| 211 void DestroyReceiver() { |
| 212 receiver_.reset(); |
| 213 } |
| 214 |
| 215 void PostDestroyReceiverTask() { |
| 216 message_loop()->task_runner()->PostTask( |
| 217 FROM_HERE, |
| 218 base::Bind(&SpeechRecognitionAudioReceiverTestBase::DestroyReceiver, |
| 219 base::Unretained(this))); |
| 220 } |
| 221 |
| 222 void RenderSampleAudioToOutputBus(int16 value) { |
| 223 const int kFrames = kOutputFramesPerBuffer * kOutputChannels; |
| 224 int16 audio_packet[kFrames]; |
| 225 for (int i = 0; i < kFrames; ++i) { |
| 226 audio_packet[i] = value; |
| 227 } |
| 228 |
| 229 output_bus_->FromInterleaved(audio_packet, |
| 230 output_bus_->frames(), |
| 231 sizeof(audio_packet[0])); |
| 232 } |
| 233 |
| 234 // Checks that target_bus has same data as the source |output_bus_|. |
| 235 void CheckAudioBus(const media::AudioBus* target_bus) { |
| 236 const int kDataLength = kOutputFramesPerBuffer * kOutputChannels; |
| 237 int16 source_data[kDataLength] = {0}; |
| 238 output_bus_->ToInterleaved(output_bus_->frames(), |
| 239 kOutputBitsPerSample / 8, |
| 240 source_data); |
| 241 int16 target_data[kDataLength] = {0}; |
| 242 target_bus->ToInterleaved(target_bus->frames(), |
| 243 kOutputBitsPerSample / 8, |
| 244 target_data); |
| 245 |
| 246 for (int i = 0 ; i < kDataLength; ++i) { |
| 247 EXPECT_EQ(source_data[i], target_data[i]); |
| 248 } |
| 249 } |
| 250 |
| 251 // How many sends before applying the test scenario. |
| 252 uint32 num_messages_; |
| 253 |
| 254 // Affects behavior of the socket and receiver after the |num_messages_| |
| 255 // messages were receieved. |
| 256 TestScenario scenario_; |
| 257 |
| 258 uint32 buffer_index_; |
| 259 |
| 260 scoped_ptr<SpeechRecognitionAudioReceiver> receiver_; |
| 261 |
| 262 scoped_ptr<base::SyncSocket> producer_socket_; |
| 263 |
| 264 scoped_ptr<base::SharedMemory> shared_memory_; |
| 265 |
| 266 scoped_ptr<media::AudioBus> output_bus_; |
| 267 |
| 268 uint32* peer_buffer_index_; |
| 269 |
| 270 DISALLOW_COPY_AND_ASSIGN(SpeechRecognitionAudioReceiverTestBase); |
| 271 }; |
| 272 |
| 273 class SpeechRecognitionAudioReceiverTest |
| 274 : public SpeechRecognitionAudioReceiverTestBase, |
| 275 public testing::WithParamInterface<TestScenario> { |
| 276 }; |
| 277 |
| 278 TEST_P(SpeechRecognitionAudioReceiverTest, CheckScenario) { |
| 279 set_scenario(GetParam()); |
| 280 SendMessages(kMessages); |
| 281 |
| 282 EXPECT_CALL(*this, SendCalled()) |
| 283 .Times(kMessages); |
| 284 EXPECT_CALL(*this, OnDataCalled()) |
| 285 .Times(kMessages); |
| 286 EXPECT_CALL(*this, OnErrorCalled()) |
| 287 .Times((scenario() & EXPECT_ERROR) ? 1 : 0); |
| 288 |
| 289 message_loop()->RunUntilIdle(); |
| 290 }; |
| 291 |
| 292 // We don't destroy the receiver after all sends were issued which will cause |
| 293 // timeouts on the socket receive and an |OnError()| callback is issued to |
| 294 // indicate there is a problem with receiving data. The session is then aborted |
| 295 // by the speech recognizer in production. |
| 296 INSTANTIATE_TEST_CASE_P( |
| 297 SendAndFreezeTriggersError, |
| 298 SpeechRecognitionAudioReceiverTest, |
| 299 testing::Values(TestScenario( |
| 300 NOT & CLOSE_SOCKET | NOT & DESTROY_RECEIVER | EXPECT_ERROR))); |
| 301 |
| 302 // Here we destroy the receiver after sending the messages which should stop the |
| 303 // receiving thread cleanly - without an error callback. This is a desired |
| 304 // behavior since the speech recognizer should destroy the receiver when the |
| 305 // session is to be stopped. |
| 306 INSTANTIATE_TEST_CASE_P( |
| 307 SendAndDestroyTriggersNoError, |
| 308 SpeechRecognitionAudioReceiverTest, |
| 309 testing::Values(TestScenario( |
| 310 NOT & CLOSE_SOCKET | DESTROY_RECEIVER | NOT & EXPECT_ERROR))); |
| 311 |
| 312 // We close the socket after sending |kMessages| messages expecting same |
| 313 // behavior as when a timeout occurs. |
| 314 INSTANTIATE_TEST_CASE_P( |
| 315 SendAndCloseSocketTriggersError, |
| 316 SpeechRecognitionAudioReceiverTest, |
| 317 testing::Values(TestScenario( |
| 318 CLOSE_SOCKET | NOT & DESTROY_RECEIVER | EXPECT_ERROR))); |
| 319 |
| 320 // We close the socket and destroy the receiver expecting same behavior as when |
| 321 // a timeout occurs. |
| 322 INSTANTIATE_TEST_CASE_P( |
| 323 SendCloseDestroyTriggersNoError, |
| 324 SpeechRecognitionAudioReceiverTest, |
| 325 testing::Values(TestScenario( |
| 326 CLOSE_SOCKET | DESTROY_RECEIVER | NOT & EXPECT_ERROR))); |
| 327 |
| 328 } // namespace content |
| OLD | NEW |