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