[refactor] More post-NSS WebCrypto cleanups (utility functions).
[chromium-blink-merge.git] / content / browser / speech / google_streaming_remote_engine_unittest.cc
blobadc8bad49ea22c139a3974257ba3dc46e452610c
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.
5 #include <queue>
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;
30 namespace content {
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 {
41 public:
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 {
57 error_ = error.code;
60 // testing::Test methods.
61 void SetUp() override;
62 void TearDown() override;
64 protected:
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());
324 // Shut down.
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
407 // created.
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;
455 return new_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(
477 downstream_fetcher,
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()));
522 results_.pop();
525 bool GoogleStreamingRemoteEngineTest::ResultsAreEqual(
526 const SpeechRecognitionResults& a, const SpeechRecognitionResults& b) {
527 if (a.size() != b.size())
528 return false;
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()) {
535 return false;
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) {
542 return false;
547 return true;
550 void GoogleStreamingRemoteEngineTest::ExpectFramedChunk(
551 const std::string& chunk, uint32_t type) {
552 uint32_t value;
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));
569 return msg_string;
572 } // namespace content