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

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

Issue 1891543002: Devirtualize SpeechRecognitionEngine (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@kill_one_shot_engine
Patch Set: drop an include Created 4 years, 8 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 (c) 2012 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 "content/browser/speech/google_streaming_remote_engine.h"
6
7 #include <stddef.h>
8 #include <stdint.h>
9
10 #include <memory>
11 #include <queue>
12
13 #include "base/big_endian.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/numerics/safe_conversions.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/sys_byteorder.h"
18 #include "content/browser/speech/audio_buffer.h"
19 #include "content/browser/speech/proto/google_streaming_api.pb.h"
20 #include "content/public/common/speech_recognition_error.h"
21 #include "content/public/common/speech_recognition_result.h"
22 #include "net/base/net_errors.h"
23 #include "net/url_request/test_url_fetcher_factory.h"
24 #include "net/url_request/url_request_context_getter.h"
25 #include "net/url_request/url_request_status.h"
26 #include "testing/gtest/include/gtest/gtest.h"
27
28 using base::HostToNet32;
29 using base::checked_cast;
30 using net::URLRequestStatus;
31 using net::TestURLFetcher;
32 using net::TestURLFetcherFactory;
33
34 namespace content {
35
36 // Frame types for framed POST data.
37 static const uint32_t kFrameTypePreamble = 0;
38 static const uint32_t kFrameTypeRecognitionAudio = 1;
39
40 // Note: the terms upstream and downstream are from the point-of-view of the
41 // client (engine_under_test_).
42
43 class GoogleStreamingRemoteEngineTest : public SpeechRecognitionEngineDelegate,
44 public testing::Test {
45 public:
46 GoogleStreamingRemoteEngineTest()
47 : last_number_of_upstream_chunks_seen_(0U),
48 error_(SPEECH_RECOGNITION_ERROR_NONE),
49 end_of_utterance_counter_(0) { }
50
51 // Creates a speech recognition request and invokes its URL fetcher delegate
52 // with the given test data.
53 void CreateAndTestRequest(bool success, const std::string& http_response);
54
55 // SpeechRecognitionRequestDelegate methods.
56 void OnSpeechRecognitionEngineResults(
57 const SpeechRecognitionResults& results) override {
58 results_.push(results);
59 }
60 void OnSpeechRecognitionEngineEndOfUtterance() override {
61 ++end_of_utterance_counter_;
62 }
63 void OnSpeechRecognitionEngineError(
64 const SpeechRecognitionError& error) override {
65 error_ = error.code;
66 }
67
68 // testing::Test methods.
69 void SetUp() override;
70 void TearDown() override;
71
72 protected:
73 enum DownstreamError {
74 DOWNSTREAM_ERROR_NONE,
75 DOWNSTREAM_ERROR_HTTP500,
76 DOWNSTREAM_ERROR_NETWORK,
77 DOWNSTREAM_ERROR_WEBSERVICE_NO_MATCH
78 };
79 static bool ResultsAreEqual(const SpeechRecognitionResults& a,
80 const SpeechRecognitionResults& b);
81 static std::string SerializeProtobufResponse(
82 const proto::SpeechRecognitionEvent& msg);
83
84 TestURLFetcher* GetUpstreamFetcher();
85 TestURLFetcher* GetDownstreamFetcher();
86 void StartMockRecognition();
87 void EndMockRecognition();
88 void InjectDummyAudioChunk();
89 size_t UpstreamChunksUploadedFromLastCall();
90 std::string LastUpstreamChunkUploaded();
91 void ProvideMockProtoResultDownstream(
92 const proto::SpeechRecognitionEvent& result);
93 void ProvideMockResultDownstream(const SpeechRecognitionResult& result);
94 void ExpectResultsReceived(const SpeechRecognitionResults& result);
95 void ExpectFramedChunk(const std::string& chunk, uint32_t type);
96 void CloseMockDownstream(DownstreamError error);
97
98 std::unique_ptr<GoogleStreamingRemoteEngine> engine_under_test_;
99 TestURLFetcherFactory url_fetcher_factory_;
100 size_t last_number_of_upstream_chunks_seen_;
101 base::MessageLoop message_loop_;
102 std::string response_buffer_;
103 SpeechRecognitionErrorCode error_;
104 int end_of_utterance_counter_;
105 std::queue<SpeechRecognitionResults> results_;
106 };
107
108 TEST_F(GoogleStreamingRemoteEngineTest, SingleDefinitiveResult) {
109 StartMockRecognition();
110 ASSERT_TRUE(GetUpstreamFetcher());
111 ASSERT_EQ(0U, UpstreamChunksUploadedFromLastCall());
112
113 // Inject some dummy audio chunks and check a corresponding chunked upload
114 // is performed every time on the server.
115 for (int i = 0; i < 3; ++i) {
116 InjectDummyAudioChunk();
117 ASSERT_EQ(1U, UpstreamChunksUploadedFromLastCall());
118 }
119
120 // Ensure that a final (empty) audio chunk is uploaded on chunks end.
121 engine_under_test_->AudioChunksEnded();
122 ASSERT_EQ(1U, UpstreamChunksUploadedFromLastCall());
123 ASSERT_TRUE(engine_under_test_->IsRecognitionPending());
124
125 // Simulate a protobuf message streamed from the server containing a single
126 // result with two hypotheses.
127 SpeechRecognitionResults results;
128 results.push_back(SpeechRecognitionResult());
129 SpeechRecognitionResult& result = results.back();
130 result.is_provisional = false;
131 result.hypotheses.push_back(
132 SpeechRecognitionHypothesis(base::UTF8ToUTF16("hypothesis 1"), 0.1F));
133 result.hypotheses.push_back(
134 SpeechRecognitionHypothesis(base::UTF8ToUTF16("hypothesis 2"), 0.2F));
135
136 ProvideMockResultDownstream(result);
137 ExpectResultsReceived(results);
138 ASSERT_TRUE(engine_under_test_->IsRecognitionPending());
139
140 // Ensure everything is closed cleanly after the downstream is closed.
141 CloseMockDownstream(DOWNSTREAM_ERROR_NONE);
142 ASSERT_FALSE(engine_under_test_->IsRecognitionPending());
143 EndMockRecognition();
144 ASSERT_EQ(SPEECH_RECOGNITION_ERROR_NONE, error_);
145 ASSERT_EQ(0U, results_.size());
146 }
147
148 TEST_F(GoogleStreamingRemoteEngineTest, SeveralStreamingResults) {
149 StartMockRecognition();
150 ASSERT_TRUE(GetUpstreamFetcher());
151 ASSERT_EQ(0U, UpstreamChunksUploadedFromLastCall());
152
153 for (int i = 0; i < 4; ++i) {
154 InjectDummyAudioChunk();
155 ASSERT_EQ(1U, UpstreamChunksUploadedFromLastCall());
156
157 SpeechRecognitionResults results;
158 results.push_back(SpeechRecognitionResult());
159 SpeechRecognitionResult& result = results.back();
160 result.is_provisional = (i % 2 == 0); // Alternate result types.
161 float confidence = result.is_provisional ? 0.0F : (i * 0.1F);
162 result.hypotheses.push_back(SpeechRecognitionHypothesis(
163 base::UTF8ToUTF16("hypothesis"), confidence));
164
165 ProvideMockResultDownstream(result);
166 ExpectResultsReceived(results);
167 ASSERT_TRUE(engine_under_test_->IsRecognitionPending());
168 }
169
170 // Ensure that a final (empty) audio chunk is uploaded on chunks end.
171 engine_under_test_->AudioChunksEnded();
172 ASSERT_EQ(1U, UpstreamChunksUploadedFromLastCall());
173 ASSERT_TRUE(engine_under_test_->IsRecognitionPending());
174
175 // Simulate a final definitive result.
176 SpeechRecognitionResults results;
177 results.push_back(SpeechRecognitionResult());
178 SpeechRecognitionResult& result = results.back();
179 result.is_provisional = false;
180 result.hypotheses.push_back(
181 SpeechRecognitionHypothesis(base::UTF8ToUTF16("The final result"), 1.0F));
182 ProvideMockResultDownstream(result);
183 ExpectResultsReceived(results);
184 ASSERT_TRUE(engine_under_test_->IsRecognitionPending());
185
186 // Ensure everything is closed cleanly after the downstream is closed.
187 CloseMockDownstream(DOWNSTREAM_ERROR_NONE);
188 ASSERT_FALSE(engine_under_test_->IsRecognitionPending());
189 EndMockRecognition();
190 ASSERT_EQ(SPEECH_RECOGNITION_ERROR_NONE, error_);
191 ASSERT_EQ(0U, results_.size());
192 }
193
194 TEST_F(GoogleStreamingRemoteEngineTest, NoFinalResultAfterAudioChunksEnded) {
195 StartMockRecognition();
196 ASSERT_TRUE(GetUpstreamFetcher());
197 ASSERT_EQ(0U, UpstreamChunksUploadedFromLastCall());
198
199 // Simulate one pushed audio chunk.
200 InjectDummyAudioChunk();
201 ASSERT_EQ(1U, UpstreamChunksUploadedFromLastCall());
202
203 // Simulate the corresponding definitive result.
204 SpeechRecognitionResults results;
205 results.push_back(SpeechRecognitionResult());
206 SpeechRecognitionResult& result = results.back();
207 result.hypotheses.push_back(
208 SpeechRecognitionHypothesis(base::UTF8ToUTF16("hypothesis"), 1.0F));
209 ProvideMockResultDownstream(result);
210 ExpectResultsReceived(results);
211 ASSERT_TRUE(engine_under_test_->IsRecognitionPending());
212
213 // Simulate a silent downstream closure after |AudioChunksEnded|.
214 engine_under_test_->AudioChunksEnded();
215 ASSERT_EQ(1U, UpstreamChunksUploadedFromLastCall());
216 ASSERT_TRUE(engine_under_test_->IsRecognitionPending());
217 CloseMockDownstream(DOWNSTREAM_ERROR_NONE);
218
219 // Expect an empty result, aimed at notifying recognition ended with no
220 // actual results nor errors.
221 SpeechRecognitionResults empty_results;
222 ExpectResultsReceived(empty_results);
223
224 // Ensure everything is closed cleanly after the downstream is closed.
225 ASSERT_FALSE(engine_under_test_->IsRecognitionPending());
226 EndMockRecognition();
227 ASSERT_EQ(SPEECH_RECOGNITION_ERROR_NONE, error_);
228 ASSERT_EQ(0U, results_.size());
229 }
230
231 TEST_F(GoogleStreamingRemoteEngineTest, NoMatchError) {
232 StartMockRecognition();
233 ASSERT_TRUE(GetUpstreamFetcher());
234 ASSERT_EQ(0U, UpstreamChunksUploadedFromLastCall());
235
236 for (int i = 0; i < 3; ++i)
237 InjectDummyAudioChunk();
238 engine_under_test_->AudioChunksEnded();
239 ASSERT_EQ(4U, UpstreamChunksUploadedFromLastCall());
240 ASSERT_TRUE(engine_under_test_->IsRecognitionPending());
241
242 // Simulate only a provisional result.
243 SpeechRecognitionResults results;
244 results.push_back(SpeechRecognitionResult());
245 SpeechRecognitionResult& result = results.back();
246 result.is_provisional = true;
247 result.hypotheses.push_back(
248 SpeechRecognitionHypothesis(base::UTF8ToUTF16("The final result"), 0.0F));
249 ProvideMockResultDownstream(result);
250 ExpectResultsReceived(results);
251 ASSERT_TRUE(engine_under_test_->IsRecognitionPending());
252
253 CloseMockDownstream(DOWNSTREAM_ERROR_WEBSERVICE_NO_MATCH);
254
255 // Expect an empty result.
256 ASSERT_FALSE(engine_under_test_->IsRecognitionPending());
257 EndMockRecognition();
258 SpeechRecognitionResults empty_result;
259 ExpectResultsReceived(empty_result);
260 }
261
262 TEST_F(GoogleStreamingRemoteEngineTest, HTTPError) {
263 StartMockRecognition();
264 ASSERT_TRUE(GetUpstreamFetcher());
265 ASSERT_EQ(0U, UpstreamChunksUploadedFromLastCall());
266
267 InjectDummyAudioChunk();
268 ASSERT_EQ(1U, UpstreamChunksUploadedFromLastCall());
269
270 // Close the downstream with a HTTP 500 error.
271 CloseMockDownstream(DOWNSTREAM_ERROR_HTTP500);
272
273 // Expect a SPEECH_RECOGNITION_ERROR_NETWORK error to be raised.
274 ASSERT_FALSE(engine_under_test_->IsRecognitionPending());
275 EndMockRecognition();
276 ASSERT_EQ(SPEECH_RECOGNITION_ERROR_NETWORK, error_);
277 ASSERT_EQ(0U, results_.size());
278 }
279
280 TEST_F(GoogleStreamingRemoteEngineTest, NetworkError) {
281 StartMockRecognition();
282 ASSERT_TRUE(GetUpstreamFetcher());
283 ASSERT_EQ(0U, UpstreamChunksUploadedFromLastCall());
284
285 InjectDummyAudioChunk();
286 ASSERT_EQ(1U, UpstreamChunksUploadedFromLastCall());
287
288 // Close the downstream fetcher simulating a network failure.
289 CloseMockDownstream(DOWNSTREAM_ERROR_NETWORK);
290
291 // Expect a SPEECH_RECOGNITION_ERROR_NETWORK error to be raised.
292 ASSERT_FALSE(engine_under_test_->IsRecognitionPending());
293 EndMockRecognition();
294 ASSERT_EQ(SPEECH_RECOGNITION_ERROR_NETWORK, error_);
295 ASSERT_EQ(0U, results_.size());
296 }
297
298 TEST_F(GoogleStreamingRemoteEngineTest, Stability) {
299 StartMockRecognition();
300 ASSERT_TRUE(GetUpstreamFetcher());
301 ASSERT_EQ(0U, UpstreamChunksUploadedFromLastCall());
302
303 // Upload a dummy audio chunk.
304 InjectDummyAudioChunk();
305 ASSERT_EQ(1U, UpstreamChunksUploadedFromLastCall());
306 engine_under_test_->AudioChunksEnded();
307
308 // Simulate a protobuf message with an intermediate result without confidence,
309 // but with stability.
310 proto::SpeechRecognitionEvent proto_event;
311 proto_event.set_status(proto::SpeechRecognitionEvent::STATUS_SUCCESS);
312 proto::SpeechRecognitionResult* proto_result = proto_event.add_result();
313 proto_result->set_stability(0.5);
314 proto::SpeechRecognitionAlternative *proto_alternative =
315 proto_result->add_alternative();
316 proto_alternative->set_transcript("foo");
317 ProvideMockProtoResultDownstream(proto_event);
318
319 // Set up expectations.
320 SpeechRecognitionResults results;
321 results.push_back(SpeechRecognitionResult());
322 SpeechRecognitionResult& result = results.back();
323 result.is_provisional = true;
324 result.hypotheses.push_back(
325 SpeechRecognitionHypothesis(base::UTF8ToUTF16("foo"), 0.5));
326
327 // Check that the protobuf generated the expected result.
328 ExpectResultsReceived(results);
329
330 // Since it was a provisional result, recognition is still pending.
331 ASSERT_TRUE(engine_under_test_->IsRecognitionPending());
332
333 // Shut down.
334 CloseMockDownstream(DOWNSTREAM_ERROR_NONE);
335 ASSERT_FALSE(engine_under_test_->IsRecognitionPending());
336 EndMockRecognition();
337
338 // Since there was no final result, we get an empty "no match" result.
339 SpeechRecognitionResults empty_result;
340 ExpectResultsReceived(empty_result);
341 ASSERT_EQ(SPEECH_RECOGNITION_ERROR_NONE, error_);
342 ASSERT_EQ(0U, results_.size());
343 }
344
345 TEST_F(GoogleStreamingRemoteEngineTest, EndOfUtterance) {
346 StartMockRecognition();
347 ASSERT_TRUE(GetUpstreamFetcher());
348
349 // Simulate a END_OF_UTTERANCE proto event with continuous true.
350 SpeechRecognitionEngine::Config config;
351 config.continuous = true;
352 engine_under_test_->SetConfig(config);
353 proto::SpeechRecognitionEvent proto_event;
354 proto_event.set_endpoint(proto::SpeechRecognitionEvent::END_OF_UTTERANCE);
355 ASSERT_EQ(0, end_of_utterance_counter_);
356 ProvideMockProtoResultDownstream(proto_event);
357 ASSERT_EQ(0, end_of_utterance_counter_);
358
359 // Simulate a END_OF_UTTERANCE proto event with continuous false.
360 config.continuous = false;
361 engine_under_test_->SetConfig(config);
362 ProvideMockProtoResultDownstream(proto_event);
363 ASSERT_EQ(1, end_of_utterance_counter_);
364
365 // Shut down.
366 CloseMockDownstream(DOWNSTREAM_ERROR_NONE);
367 EndMockRecognition();
368 }
369
370 TEST_F(GoogleStreamingRemoteEngineTest, SendPreamble) {
371 const size_t kPreambleLength = 100;
372 scoped_refptr<SpeechRecognitionSessionPreamble> preamble =
373 new SpeechRecognitionSessionPreamble();
374 preamble->sample_rate = 16000;
375 preamble->sample_depth = 2;
376 preamble->sample_data.assign(kPreambleLength, 0);
377 SpeechRecognitionEngine::Config config;
378 config.auth_token = "foo";
379 config.auth_scope = "bar";
380 config.preamble = preamble;
381 engine_under_test_->SetConfig(config);
382
383 StartMockRecognition();
384 ASSERT_TRUE(GetUpstreamFetcher());
385 // First chunk uploaded should be the preamble.
386 ASSERT_EQ(1U, UpstreamChunksUploadedFromLastCall());
387 std::string chunk = LastUpstreamChunkUploaded();
388 ExpectFramedChunk(chunk, kFrameTypePreamble);
389
390 for (int i = 0; i < 3; ++i) {
391 InjectDummyAudioChunk();
392 ASSERT_EQ(1U, UpstreamChunksUploadedFromLastCall());
393 chunk = LastUpstreamChunkUploaded();
394 ExpectFramedChunk(chunk, kFrameTypeRecognitionAudio);
395 }
396 engine_under_test_->AudioChunksEnded();
397 ASSERT_TRUE(engine_under_test_->IsRecognitionPending());
398
399 // Simulate a protobuf message streamed from the server containing a single
400 // result with one hypotheses.
401 SpeechRecognitionResults results;
402 results.push_back(SpeechRecognitionResult());
403 SpeechRecognitionResult& result = results.back();
404 result.is_provisional = false;
405 result.hypotheses.push_back(
406 SpeechRecognitionHypothesis(base::UTF8ToUTF16("hypothesis 1"), 0.1F));
407
408 ProvideMockResultDownstream(result);
409 ExpectResultsReceived(results);
410 ASSERT_TRUE(engine_under_test_->IsRecognitionPending());
411
412 // Ensure everything is closed cleanly after the downstream is closed.
413 CloseMockDownstream(DOWNSTREAM_ERROR_NONE);
414 ASSERT_FALSE(engine_under_test_->IsRecognitionPending());
415 EndMockRecognition();
416 ASSERT_EQ(SPEECH_RECOGNITION_ERROR_NONE, error_);
417 ASSERT_EQ(0U, results_.size());
418 }
419
420 void GoogleStreamingRemoteEngineTest::SetUp() {
421 engine_under_test_.reset(
422 new GoogleStreamingRemoteEngine(NULL /*URLRequestContextGetter*/));
423 engine_under_test_->set_delegate(this);
424 }
425
426 void GoogleStreamingRemoteEngineTest::TearDown() {
427 engine_under_test_.reset();
428 }
429
430 TestURLFetcher* GoogleStreamingRemoteEngineTest::GetUpstreamFetcher() {
431 return url_fetcher_factory_.GetFetcherByID(
432 GoogleStreamingRemoteEngine::kUpstreamUrlFetcherIdForTesting);
433 }
434
435 TestURLFetcher* GoogleStreamingRemoteEngineTest::GetDownstreamFetcher() {
436 return url_fetcher_factory_.GetFetcherByID(
437 GoogleStreamingRemoteEngine::kDownstreamUrlFetcherIdForTesting);
438 }
439
440 // Starts recognition on the engine, ensuring that both stream fetchers are
441 // created.
442 void GoogleStreamingRemoteEngineTest::StartMockRecognition() {
443 DCHECK(engine_under_test_.get());
444
445 ASSERT_FALSE(engine_under_test_->IsRecognitionPending());
446
447 engine_under_test_->StartRecognition();
448 ASSERT_TRUE(engine_under_test_->IsRecognitionPending());
449
450 TestURLFetcher* upstream_fetcher = GetUpstreamFetcher();
451 ASSERT_TRUE(upstream_fetcher);
452 upstream_fetcher->set_url(upstream_fetcher->GetOriginalURL());
453
454 TestURLFetcher* downstream_fetcher = GetDownstreamFetcher();
455 ASSERT_TRUE(downstream_fetcher);
456 downstream_fetcher->set_url(downstream_fetcher->GetOriginalURL());
457 }
458
459 void GoogleStreamingRemoteEngineTest::EndMockRecognition() {
460 DCHECK(engine_under_test_.get());
461 engine_under_test_->EndRecognition();
462 ASSERT_FALSE(engine_under_test_->IsRecognitionPending());
463
464 // TODO(primiano): In order to be very pedantic we should check that both the
465 // upstream and downstream URL fetchers have been disposed at this time.
466 // Unfortunately it seems that there is no direct way to detect (in tests)
467 // if a url_fetcher has been freed or not, since they are not automatically
468 // de-registered from the TestURLFetcherFactory on destruction.
469 }
470
471 void GoogleStreamingRemoteEngineTest::InjectDummyAudioChunk() {
472 unsigned char dummy_audio_buffer_data[2] = {'\0', '\0'};
473 scoped_refptr<AudioChunk> dummy_audio_chunk(
474 new AudioChunk(&dummy_audio_buffer_data[0],
475 sizeof(dummy_audio_buffer_data),
476 2 /* bytes per sample */));
477 DCHECK(engine_under_test_.get());
478 engine_under_test_->TakeAudioChunk(*dummy_audio_chunk.get());
479 }
480
481 size_t GoogleStreamingRemoteEngineTest::UpstreamChunksUploadedFromLastCall() {
482 TestURLFetcher* upstream_fetcher = GetUpstreamFetcher();
483 DCHECK(upstream_fetcher);
484 const size_t number_of_chunks = upstream_fetcher->upload_chunks().size();
485 DCHECK_GE(number_of_chunks, last_number_of_upstream_chunks_seen_);
486 const size_t new_chunks = number_of_chunks -
487 last_number_of_upstream_chunks_seen_;
488 last_number_of_upstream_chunks_seen_ = number_of_chunks;
489 return new_chunks;
490 }
491
492 std::string GoogleStreamingRemoteEngineTest::LastUpstreamChunkUploaded() {
493 TestURLFetcher* upstream_fetcher = GetUpstreamFetcher();
494 DCHECK(upstream_fetcher);
495 DCHECK(!upstream_fetcher->upload_chunks().empty());
496 return upstream_fetcher->upload_chunks().back();
497 }
498
499 void GoogleStreamingRemoteEngineTest::ProvideMockProtoResultDownstream(
500 const proto::SpeechRecognitionEvent& result) {
501 TestURLFetcher* downstream_fetcher = GetDownstreamFetcher();
502
503 ASSERT_TRUE(downstream_fetcher);
504 downstream_fetcher->set_status(URLRequestStatus(/* default=SUCCESS */));
505 downstream_fetcher->set_response_code(200);
506
507 std::string response_string = SerializeProtobufResponse(result);
508 response_buffer_.append(response_string);
509 downstream_fetcher->SetResponseString(response_buffer_);
510 downstream_fetcher->delegate()->OnURLFetchDownloadProgress(
511 downstream_fetcher,
512 response_buffer_.size(),
513 -1 /* total response length not used */);
514 }
515
516 void GoogleStreamingRemoteEngineTest::ProvideMockResultDownstream(
517 const SpeechRecognitionResult& result) {
518 proto::SpeechRecognitionEvent proto_event;
519 proto_event.set_status(proto::SpeechRecognitionEvent::STATUS_SUCCESS);
520 proto::SpeechRecognitionResult* proto_result = proto_event.add_result();
521 proto_result->set_final(!result.is_provisional);
522 for (size_t i = 0; i < result.hypotheses.size(); ++i) {
523 proto::SpeechRecognitionAlternative* proto_alternative =
524 proto_result->add_alternative();
525 const SpeechRecognitionHypothesis& hypothesis = result.hypotheses[i];
526 proto_alternative->set_confidence(hypothesis.confidence);
527 proto_alternative->set_transcript(base::UTF16ToUTF8(hypothesis.utterance));
528 }
529 ProvideMockProtoResultDownstream(proto_event);
530 }
531
532 void GoogleStreamingRemoteEngineTest::CloseMockDownstream(
533 DownstreamError error) {
534 TestURLFetcher* downstream_fetcher = GetDownstreamFetcher();
535 ASSERT_TRUE(downstream_fetcher);
536
537 const net::Error net_error =
538 (error == DOWNSTREAM_ERROR_NETWORK) ? net::ERR_FAILED : net::OK;
539 downstream_fetcher->set_status(URLRequestStatus::FromError(net_error));
540 downstream_fetcher->set_response_code(
541 (error == DOWNSTREAM_ERROR_HTTP500) ? 500 : 200);
542
543 if (error == DOWNSTREAM_ERROR_WEBSERVICE_NO_MATCH) {
544 // Send empty response.
545 proto::SpeechRecognitionEvent response;
546 response_buffer_.append(SerializeProtobufResponse(response));
547 }
548 downstream_fetcher->SetResponseString(response_buffer_);
549 downstream_fetcher->delegate()->OnURLFetchComplete(downstream_fetcher);
550 }
551
552 void GoogleStreamingRemoteEngineTest::ExpectResultsReceived(
553 const SpeechRecognitionResults& results) {
554 ASSERT_GE(1U, results_.size());
555 ASSERT_TRUE(ResultsAreEqual(results, results_.front()));
556 results_.pop();
557 }
558
559 bool GoogleStreamingRemoteEngineTest::ResultsAreEqual(
560 const SpeechRecognitionResults& a, const SpeechRecognitionResults& b) {
561 if (a.size() != b.size())
562 return false;
563
564 SpeechRecognitionResults::const_iterator it_a = a.begin();
565 SpeechRecognitionResults::const_iterator it_b = b.begin();
566 for (; it_a != a.end() && it_b != b.end(); ++it_a, ++it_b) {
567 if (it_a->is_provisional != it_b->is_provisional ||
568 it_a->hypotheses.size() != it_b->hypotheses.size()) {
569 return false;
570 }
571 for (size_t i = 0; i < it_a->hypotheses.size(); ++i) {
572 const SpeechRecognitionHypothesis& hyp_a = it_a->hypotheses[i];
573 const SpeechRecognitionHypothesis& hyp_b = it_b->hypotheses[i];
574 if (hyp_a.utterance != hyp_b.utterance ||
575 hyp_a.confidence != hyp_b.confidence) {
576 return false;
577 }
578 }
579 }
580
581 return true;
582 }
583
584 void GoogleStreamingRemoteEngineTest::ExpectFramedChunk(
585 const std::string& chunk, uint32_t type) {
586 uint32_t value;
587 base::ReadBigEndian(&chunk[0], &value);
588 EXPECT_EQ(chunk.size() - 8, value);
589 base::ReadBigEndian(&chunk[4], &value);
590 EXPECT_EQ(type, value);
591 }
592
593 std::string GoogleStreamingRemoteEngineTest::SerializeProtobufResponse(
594 const proto::SpeechRecognitionEvent& msg) {
595 std::string msg_string;
596 msg.SerializeToString(&msg_string);
597
598 // Prepend 4 byte prefix length indication to the protobuf message as
599 // envisaged by the google streaming recognition webservice protocol.
600 uint32_t prefix = HostToNet32(checked_cast<uint32_t>(msg_string.size()));
601 msg_string.insert(0, reinterpret_cast<char*>(&prefix), sizeof(prefix));
602
603 return msg_string;
604 }
605
606 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698