Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(104)

Side by Side Diff: content/browser/speech/speech_recognition_audio_receiver_unittest.cc

Issue 549373003: Binding media stream audio track to speech recognition [browser] Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Audio Receiver unit test Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698