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