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/url_request/test_url_fetcher_factory.h"
19 #include "net/url_request/url_request_context_getter.h"
20 #include "net/url_request/url_request_status.h"
21 #include "testing/gtest/include/gtest/gtest.h"
23 using base::HostToNet32
;
24 using base::checked_cast
;
25 using net::URLRequestStatus
;
26 using net::TestURLFetcher
;
27 using net::TestURLFetcherFactory
;
31 // Frame types for framed POST data.
32 static const uint32_t kFrameTypePreamble
= 0;
33 static const uint32_t kFrameTypeRecognitionAudio
= 1;
35 // Note: the terms upstream and downstream are from the point-of-view of the
36 // client (engine_under_test_).
38 class GoogleStreamingRemoteEngineTest
: public SpeechRecognitionEngineDelegate
,
39 public testing::Test
{
41 GoogleStreamingRemoteEngineTest()
42 : last_number_of_upstream_chunks_seen_(0U),
43 error_(SPEECH_RECOGNITION_ERROR_NONE
) { }
45 // Creates a speech recognition request and invokes its URL fetcher delegate
46 // with the given test data.
47 void CreateAndTestRequest(bool success
, const std::string
& http_response
);
49 // SpeechRecognitionRequestDelegate methods.
50 void OnSpeechRecognitionEngineResults(
51 const SpeechRecognitionResults
& results
) override
{
52 results_
.push(results
);
54 void OnSpeechRecognitionEngineError(
55 const SpeechRecognitionError
& error
) override
{
59 // testing::Test methods.
60 void SetUp() override
;
61 void TearDown() override
;
64 enum DownstreamError
{
65 DOWNSTREAM_ERROR_NONE
,
66 DOWNSTREAM_ERROR_HTTP500
,
67 DOWNSTREAM_ERROR_NETWORK
,
68 DOWNSTREAM_ERROR_WEBSERVICE_NO_MATCH
70 static bool ResultsAreEqual(const SpeechRecognitionResults
& a
,
71 const SpeechRecognitionResults
& b
);
72 static std::string
SerializeProtobufResponse(
73 const proto::SpeechRecognitionEvent
& msg
);
75 TestURLFetcher
* GetUpstreamFetcher();
76 TestURLFetcher
* GetDownstreamFetcher();
77 void StartMockRecognition();
78 void EndMockRecognition();
79 void InjectDummyAudioChunk();
80 size_t UpstreamChunksUploadedFromLastCall();
81 std::string
LastUpstreamChunkUploaded();
82 void ProvideMockProtoResultDownstream(
83 const proto::SpeechRecognitionEvent
& result
);
84 void ProvideMockResultDownstream(const SpeechRecognitionResult
& result
);
85 void ExpectResultsReceived(const SpeechRecognitionResults
& result
);
86 void ExpectFramedChunk(const std::string
& chunk
, uint32_t type
);
87 void CloseMockDownstream(DownstreamError error
);
89 scoped_ptr
<GoogleStreamingRemoteEngine
> engine_under_test_
;
90 TestURLFetcherFactory url_fetcher_factory_
;
91 size_t last_number_of_upstream_chunks_seen_
;
92 base::MessageLoop message_loop_
;
93 std::string response_buffer_
;
94 SpeechRecognitionErrorCode error_
;
95 std::queue
<SpeechRecognitionResults
> results_
;
98 TEST_F(GoogleStreamingRemoteEngineTest
, SingleDefinitiveResult
) {
99 StartMockRecognition();
100 ASSERT_TRUE(GetUpstreamFetcher());
101 ASSERT_EQ(0U, UpstreamChunksUploadedFromLastCall());
103 // Inject some dummy audio chunks and check a corresponding chunked upload
104 // is performed every time on the server.
105 for (int i
= 0; i
< 3; ++i
) {
106 InjectDummyAudioChunk();
107 ASSERT_EQ(1U, UpstreamChunksUploadedFromLastCall());
110 // Ensure that a final (empty) audio chunk is uploaded on chunks end.
111 engine_under_test_
->AudioChunksEnded();
112 ASSERT_EQ(1U, UpstreamChunksUploadedFromLastCall());
113 ASSERT_TRUE(engine_under_test_
->IsRecognitionPending());
115 // Simulate a protobuf message streamed from the server containing a single
116 // result with two hypotheses.
117 SpeechRecognitionResults results
;
118 results
.push_back(SpeechRecognitionResult());
119 SpeechRecognitionResult
& result
= results
.back();
120 result
.is_provisional
= false;
121 result
.hypotheses
.push_back(
122 SpeechRecognitionHypothesis(base::UTF8ToUTF16("hypothesis 1"), 0.1F
));
123 result
.hypotheses
.push_back(
124 SpeechRecognitionHypothesis(base::UTF8ToUTF16("hypothesis 2"), 0.2F
));
126 ProvideMockResultDownstream(result
);
127 ExpectResultsReceived(results
);
128 ASSERT_TRUE(engine_under_test_
->IsRecognitionPending());
130 // Ensure everything is closed cleanly after the downstream is closed.
131 CloseMockDownstream(DOWNSTREAM_ERROR_NONE
);
132 ASSERT_FALSE(engine_under_test_
->IsRecognitionPending());
133 EndMockRecognition();
134 ASSERT_EQ(SPEECH_RECOGNITION_ERROR_NONE
, error_
);
135 ASSERT_EQ(0U, results_
.size());
138 TEST_F(GoogleStreamingRemoteEngineTest
, SeveralStreamingResults
) {
139 StartMockRecognition();
140 ASSERT_TRUE(GetUpstreamFetcher());
141 ASSERT_EQ(0U, UpstreamChunksUploadedFromLastCall());
143 for (int i
= 0; i
< 4; ++i
) {
144 InjectDummyAudioChunk();
145 ASSERT_EQ(1U, UpstreamChunksUploadedFromLastCall());
147 SpeechRecognitionResults results
;
148 results
.push_back(SpeechRecognitionResult());
149 SpeechRecognitionResult
& result
= results
.back();
150 result
.is_provisional
= (i
% 2 == 0); // Alternate result types.
151 float confidence
= result
.is_provisional
? 0.0F
: (i
* 0.1F
);
152 result
.hypotheses
.push_back(SpeechRecognitionHypothesis(
153 base::UTF8ToUTF16("hypothesis"), confidence
));
155 ProvideMockResultDownstream(result
);
156 ExpectResultsReceived(results
);
157 ASSERT_TRUE(engine_under_test_
->IsRecognitionPending());
160 // Ensure that a final (empty) audio chunk is uploaded on chunks end.
161 engine_under_test_
->AudioChunksEnded();
162 ASSERT_EQ(1U, UpstreamChunksUploadedFromLastCall());
163 ASSERT_TRUE(engine_under_test_
->IsRecognitionPending());
165 // Simulate a final definitive result.
166 SpeechRecognitionResults results
;
167 results
.push_back(SpeechRecognitionResult());
168 SpeechRecognitionResult
& result
= results
.back();
169 result
.is_provisional
= false;
170 result
.hypotheses
.push_back(
171 SpeechRecognitionHypothesis(base::UTF8ToUTF16("The final result"), 1.0F
));
172 ProvideMockResultDownstream(result
);
173 ExpectResultsReceived(results
);
174 ASSERT_TRUE(engine_under_test_
->IsRecognitionPending());
176 // Ensure everything is closed cleanly after the downstream is closed.
177 CloseMockDownstream(DOWNSTREAM_ERROR_NONE
);
178 ASSERT_FALSE(engine_under_test_
->IsRecognitionPending());
179 EndMockRecognition();
180 ASSERT_EQ(SPEECH_RECOGNITION_ERROR_NONE
, error_
);
181 ASSERT_EQ(0U, results_
.size());
184 TEST_F(GoogleStreamingRemoteEngineTest
, NoFinalResultAfterAudioChunksEnded
) {
185 StartMockRecognition();
186 ASSERT_TRUE(GetUpstreamFetcher());
187 ASSERT_EQ(0U, UpstreamChunksUploadedFromLastCall());
189 // Simulate one pushed audio chunk.
190 InjectDummyAudioChunk();
191 ASSERT_EQ(1U, UpstreamChunksUploadedFromLastCall());
193 // Simulate the corresponding definitive result.
194 SpeechRecognitionResults results
;
195 results
.push_back(SpeechRecognitionResult());
196 SpeechRecognitionResult
& result
= results
.back();
197 result
.hypotheses
.push_back(
198 SpeechRecognitionHypothesis(base::UTF8ToUTF16("hypothesis"), 1.0F
));
199 ProvideMockResultDownstream(result
);
200 ExpectResultsReceived(results
);
201 ASSERT_TRUE(engine_under_test_
->IsRecognitionPending());
203 // Simulate a silent downstream closure after |AudioChunksEnded|.
204 engine_under_test_
->AudioChunksEnded();
205 ASSERT_EQ(1U, UpstreamChunksUploadedFromLastCall());
206 ASSERT_TRUE(engine_under_test_
->IsRecognitionPending());
207 CloseMockDownstream(DOWNSTREAM_ERROR_NONE
);
209 // Expect an empty result, aimed at notifying recognition ended with no
210 // actual results nor errors.
211 SpeechRecognitionResults empty_results
;
212 ExpectResultsReceived(empty_results
);
214 // Ensure everything is closed cleanly after the downstream is closed.
215 ASSERT_FALSE(engine_under_test_
->IsRecognitionPending());
216 EndMockRecognition();
217 ASSERT_EQ(SPEECH_RECOGNITION_ERROR_NONE
, error_
);
218 ASSERT_EQ(0U, results_
.size());
221 TEST_F(GoogleStreamingRemoteEngineTest
, NoMatchError
) {
222 StartMockRecognition();
223 ASSERT_TRUE(GetUpstreamFetcher());
224 ASSERT_EQ(0U, UpstreamChunksUploadedFromLastCall());
226 for (int i
= 0; i
< 3; ++i
)
227 InjectDummyAudioChunk();
228 engine_under_test_
->AudioChunksEnded();
229 ASSERT_EQ(4U, UpstreamChunksUploadedFromLastCall());
230 ASSERT_TRUE(engine_under_test_
->IsRecognitionPending());
232 // Simulate only a provisional result.
233 SpeechRecognitionResults results
;
234 results
.push_back(SpeechRecognitionResult());
235 SpeechRecognitionResult
& result
= results
.back();
236 result
.is_provisional
= true;
237 result
.hypotheses
.push_back(
238 SpeechRecognitionHypothesis(base::UTF8ToUTF16("The final result"), 0.0F
));
239 ProvideMockResultDownstream(result
);
240 ExpectResultsReceived(results
);
241 ASSERT_TRUE(engine_under_test_
->IsRecognitionPending());
243 CloseMockDownstream(DOWNSTREAM_ERROR_WEBSERVICE_NO_MATCH
);
245 // Expect an empty result.
246 ASSERT_FALSE(engine_under_test_
->IsRecognitionPending());
247 EndMockRecognition();
248 SpeechRecognitionResults empty_result
;
249 ExpectResultsReceived(empty_result
);
252 TEST_F(GoogleStreamingRemoteEngineTest
, HTTPError
) {
253 StartMockRecognition();
254 ASSERT_TRUE(GetUpstreamFetcher());
255 ASSERT_EQ(0U, UpstreamChunksUploadedFromLastCall());
257 InjectDummyAudioChunk();
258 ASSERT_EQ(1U, UpstreamChunksUploadedFromLastCall());
260 // Close the downstream with a HTTP 500 error.
261 CloseMockDownstream(DOWNSTREAM_ERROR_HTTP500
);
263 // Expect a SPEECH_RECOGNITION_ERROR_NETWORK error to be raised.
264 ASSERT_FALSE(engine_under_test_
->IsRecognitionPending());
265 EndMockRecognition();
266 ASSERT_EQ(SPEECH_RECOGNITION_ERROR_NETWORK
, error_
);
267 ASSERT_EQ(0U, results_
.size());
270 TEST_F(GoogleStreamingRemoteEngineTest
, NetworkError
) {
271 StartMockRecognition();
272 ASSERT_TRUE(GetUpstreamFetcher());
273 ASSERT_EQ(0U, UpstreamChunksUploadedFromLastCall());
275 InjectDummyAudioChunk();
276 ASSERT_EQ(1U, UpstreamChunksUploadedFromLastCall());
278 // Close the downstream fetcher simulating a network failure.
279 CloseMockDownstream(DOWNSTREAM_ERROR_NETWORK
);
281 // Expect a SPEECH_RECOGNITION_ERROR_NETWORK error to be raised.
282 ASSERT_FALSE(engine_under_test_
->IsRecognitionPending());
283 EndMockRecognition();
284 ASSERT_EQ(SPEECH_RECOGNITION_ERROR_NETWORK
, error_
);
285 ASSERT_EQ(0U, results_
.size());
288 TEST_F(GoogleStreamingRemoteEngineTest
, Stability
) {
289 StartMockRecognition();
290 ASSERT_TRUE(GetUpstreamFetcher());
291 ASSERT_EQ(0U, UpstreamChunksUploadedFromLastCall());
293 // Upload a dummy audio chunk.
294 InjectDummyAudioChunk();
295 ASSERT_EQ(1U, UpstreamChunksUploadedFromLastCall());
296 engine_under_test_
->AudioChunksEnded();
298 // Simulate a protobuf message with an intermediate result without confidence,
299 // but with stability.
300 proto::SpeechRecognitionEvent proto_event
;
301 proto_event
.set_status(proto::SpeechRecognitionEvent::STATUS_SUCCESS
);
302 proto::SpeechRecognitionResult
* proto_result
= proto_event
.add_result();
303 proto_result
->set_stability(0.5);
304 proto::SpeechRecognitionAlternative
*proto_alternative
=
305 proto_result
->add_alternative();
306 proto_alternative
->set_transcript("foo");
307 ProvideMockProtoResultDownstream(proto_event
);
309 // Set up expectations.
310 SpeechRecognitionResults results
;
311 results
.push_back(SpeechRecognitionResult());
312 SpeechRecognitionResult
& result
= results
.back();
313 result
.is_provisional
= true;
314 result
.hypotheses
.push_back(
315 SpeechRecognitionHypothesis(base::UTF8ToUTF16("foo"), 0.5));
317 // Check that the protobuf generated the expected result.
318 ExpectResultsReceived(results
);
320 // Since it was a provisional result, recognition is still pending.
321 ASSERT_TRUE(engine_under_test_
->IsRecognitionPending());
324 CloseMockDownstream(DOWNSTREAM_ERROR_NONE
);
325 ASSERT_FALSE(engine_under_test_
->IsRecognitionPending());
326 EndMockRecognition();
328 // Since there was no final result, we get an empty "no match" result.
329 SpeechRecognitionResults empty_result
;
330 ExpectResultsReceived(empty_result
);
331 ASSERT_EQ(SPEECH_RECOGNITION_ERROR_NONE
, error_
);
332 ASSERT_EQ(0U, results_
.size());
335 TEST_F(GoogleStreamingRemoteEngineTest
, SendPreamble
) {
336 const size_t kPreambleLength
= 100;
337 scoped_refptr
<SpeechRecognitionSessionPreamble
> preamble
=
338 new SpeechRecognitionSessionPreamble();
339 preamble
->sample_rate
= 16000;
340 preamble
->sample_depth
= 2;
341 preamble
->sample_data
.assign(kPreambleLength
, 0);
342 SpeechRecognitionEngine::Config config
;
343 config
.auth_token
= "foo";
344 config
.auth_scope
= "bar";
345 config
.preamble
= preamble
;
346 engine_under_test_
->SetConfig(config
);
348 StartMockRecognition();
349 ASSERT_TRUE(GetUpstreamFetcher());
350 // First chunk uploaded should be the preamble.
351 ASSERT_EQ(1U, UpstreamChunksUploadedFromLastCall());
352 std::string chunk
= LastUpstreamChunkUploaded();
353 ExpectFramedChunk(chunk
, kFrameTypePreamble
);
355 for (int i
= 0; i
< 3; ++i
) {
356 InjectDummyAudioChunk();
357 ASSERT_EQ(1U, UpstreamChunksUploadedFromLastCall());
358 chunk
= LastUpstreamChunkUploaded();
359 ExpectFramedChunk(chunk
, kFrameTypeRecognitionAudio
);
361 engine_under_test_
->AudioChunksEnded();
362 ASSERT_TRUE(engine_under_test_
->IsRecognitionPending());
364 // Simulate a protobuf message streamed from the server containing a single
365 // result with one hypotheses.
366 SpeechRecognitionResults results
;
367 results
.push_back(SpeechRecognitionResult());
368 SpeechRecognitionResult
& result
= results
.back();
369 result
.is_provisional
= false;
370 result
.hypotheses
.push_back(
371 SpeechRecognitionHypothesis(base::UTF8ToUTF16("hypothesis 1"), 0.1F
));
373 ProvideMockResultDownstream(result
);
374 ExpectResultsReceived(results
);
375 ASSERT_TRUE(engine_under_test_
->IsRecognitionPending());
377 // Ensure everything is closed cleanly after the downstream is closed.
378 CloseMockDownstream(DOWNSTREAM_ERROR_NONE
);
379 ASSERT_FALSE(engine_under_test_
->IsRecognitionPending());
380 EndMockRecognition();
381 ASSERT_EQ(SPEECH_RECOGNITION_ERROR_NONE
, error_
);
382 ASSERT_EQ(0U, results_
.size());
385 void GoogleStreamingRemoteEngineTest::SetUp() {
386 engine_under_test_
.reset(
387 new GoogleStreamingRemoteEngine(NULL
/*URLRequestContextGetter*/));
388 engine_under_test_
->set_delegate(this);
391 void GoogleStreamingRemoteEngineTest::TearDown() {
392 engine_under_test_
.reset();
395 TestURLFetcher
* GoogleStreamingRemoteEngineTest::GetUpstreamFetcher() {
396 return url_fetcher_factory_
.GetFetcherByID(
397 GoogleStreamingRemoteEngine::kUpstreamUrlFetcherIdForTesting
);
400 TestURLFetcher
* GoogleStreamingRemoteEngineTest::GetDownstreamFetcher() {
401 return url_fetcher_factory_
.GetFetcherByID(
402 GoogleStreamingRemoteEngine::kDownstreamUrlFetcherIdForTesting
);
405 // Starts recognition on the engine, ensuring that both stream fetchers are
407 void GoogleStreamingRemoteEngineTest::StartMockRecognition() {
408 DCHECK(engine_under_test_
.get());
410 ASSERT_FALSE(engine_under_test_
->IsRecognitionPending());
412 engine_under_test_
->StartRecognition();
413 ASSERT_TRUE(engine_under_test_
->IsRecognitionPending());
415 TestURLFetcher
* upstream_fetcher
= GetUpstreamFetcher();
416 ASSERT_TRUE(upstream_fetcher
);
417 upstream_fetcher
->set_url(upstream_fetcher
->GetOriginalURL());
419 TestURLFetcher
* downstream_fetcher
= GetDownstreamFetcher();
420 ASSERT_TRUE(downstream_fetcher
);
421 downstream_fetcher
->set_url(downstream_fetcher
->GetOriginalURL());
424 void GoogleStreamingRemoteEngineTest::EndMockRecognition() {
425 DCHECK(engine_under_test_
.get());
426 engine_under_test_
->EndRecognition();
427 ASSERT_FALSE(engine_under_test_
->IsRecognitionPending());
429 // TODO(primiano): In order to be very pedantic we should check that both the
430 // upstream and downstream URL fetchers have been disposed at this time.
431 // Unfortunately it seems that there is no direct way to detect (in tests)
432 // if a url_fetcher has been freed or not, since they are not automatically
433 // de-registered from the TestURLFetcherFactory on destruction.
436 void GoogleStreamingRemoteEngineTest::InjectDummyAudioChunk() {
437 unsigned char dummy_audio_buffer_data
[2] = {'\0', '\0'};
438 scoped_refptr
<AudioChunk
> dummy_audio_chunk(
439 new AudioChunk(&dummy_audio_buffer_data
[0],
440 sizeof(dummy_audio_buffer_data
),
441 2 /* bytes per sample */));
442 DCHECK(engine_under_test_
.get());
443 engine_under_test_
->TakeAudioChunk(*dummy_audio_chunk
.get());
446 size_t GoogleStreamingRemoteEngineTest::UpstreamChunksUploadedFromLastCall() {
447 TestURLFetcher
* upstream_fetcher
= GetUpstreamFetcher();
448 DCHECK(upstream_fetcher
);
449 const size_t number_of_chunks
= upstream_fetcher
->upload_chunks().size();
450 DCHECK_GE(number_of_chunks
, last_number_of_upstream_chunks_seen_
);
451 const size_t new_chunks
= number_of_chunks
-
452 last_number_of_upstream_chunks_seen_
;
453 last_number_of_upstream_chunks_seen_
= number_of_chunks
;
457 std::string
GoogleStreamingRemoteEngineTest::LastUpstreamChunkUploaded() {
458 TestURLFetcher
* upstream_fetcher
= GetUpstreamFetcher();
459 DCHECK(upstream_fetcher
);
460 DCHECK(!upstream_fetcher
->upload_chunks().empty());
461 return upstream_fetcher
->upload_chunks().back();
464 void GoogleStreamingRemoteEngineTest::ProvideMockProtoResultDownstream(
465 const proto::SpeechRecognitionEvent
& result
) {
466 TestURLFetcher
* downstream_fetcher
= GetDownstreamFetcher();
468 ASSERT_TRUE(downstream_fetcher
);
469 downstream_fetcher
->set_status(URLRequestStatus(/* default=SUCCESS */));
470 downstream_fetcher
->set_response_code(200);
472 std::string response_string
= SerializeProtobufResponse(result
);
473 response_buffer_
.append(response_string
);
474 downstream_fetcher
->SetResponseString(response_buffer_
);
475 downstream_fetcher
->delegate()->OnURLFetchDownloadProgress(
477 response_buffer_
.size(),
478 -1 /* total response length not used */);
481 void GoogleStreamingRemoteEngineTest::ProvideMockResultDownstream(
482 const SpeechRecognitionResult
& result
) {
483 proto::SpeechRecognitionEvent proto_event
;
484 proto_event
.set_status(proto::SpeechRecognitionEvent::STATUS_SUCCESS
);
485 proto::SpeechRecognitionResult
* proto_result
= proto_event
.add_result();
486 proto_result
->set_final(!result
.is_provisional
);
487 for (size_t i
= 0; i
< result
.hypotheses
.size(); ++i
) {
488 proto::SpeechRecognitionAlternative
* proto_alternative
=
489 proto_result
->add_alternative();
490 const SpeechRecognitionHypothesis
& hypothesis
= result
.hypotheses
[i
];
491 proto_alternative
->set_confidence(hypothesis
.confidence
);
492 proto_alternative
->set_transcript(base::UTF16ToUTF8(hypothesis
.utterance
));
494 ProvideMockProtoResultDownstream(proto_event
);
497 void GoogleStreamingRemoteEngineTest::CloseMockDownstream(
498 DownstreamError error
) {
499 TestURLFetcher
* downstream_fetcher
= GetDownstreamFetcher();
500 ASSERT_TRUE(downstream_fetcher
);
502 const URLRequestStatus::Status fetcher_status
=
503 (error
== DOWNSTREAM_ERROR_NETWORK
) ? URLRequestStatus::FAILED
:
504 URLRequestStatus::SUCCESS
;
505 downstream_fetcher
->set_status(URLRequestStatus(fetcher_status
, 0));
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