18bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
28bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
38bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)// found in the LICENSE file.
48bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
58bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#include "content/test/mock_google_streaming_server.h"
68bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
78bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#include "base/bind.h"
85d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/numerics/safe_conversions.h"
98bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#include "base/strings/string_util.h"
108bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#include "base/strings/utf_string_conversions.h"
118bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#include "base/sys_byteorder.h"
128bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#include "base/values.h"
138bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#include "content/browser/speech/google_streaming_remote_engine.h"
148bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#include "content/browser/speech/proto/google_streaming_api.pb.h"
158bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#include "content/browser/speech/speech_recognition_manager_impl.h"
168bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#include "net/base/escape.h"
178bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#include "net/url_request/url_fetcher_delegate.h"
188bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#include "net/url_request/url_request_status.h"
198bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
208bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)using base::HostToNet32;
215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)using base::checked_cast;
228bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
238bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)namespace content {
248bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
258bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)MockGoogleStreamingServer::MockGoogleStreamingServer(Delegate* delegate)
268bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    : delegate_(delegate),
278bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      kDownstreamUrlFetcherId(
288bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)          GoogleStreamingRemoteEngine::kDownstreamUrlFetcherIdForTesting),
298bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      kUpstreamUrlFetcherId(
308bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)          GoogleStreamingRemoteEngine::kUpstreamUrlFetcherIdForTesting) {
318bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  url_fetcher_factory_.SetDelegateForTests(this);
328bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)}
338bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
348bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)MockGoogleStreamingServer::~MockGoogleStreamingServer() {
358bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)}
368bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
378bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)void MockGoogleStreamingServer::OnRequestStart(int fetcher_id) {
388bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  if (fetcher_id != kDownstreamUrlFetcherId)
398bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    return;
408bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
418bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // Extract request argument from the the request URI.
428bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  std::string query = GetURLFetcher(true)->GetOriginalURL().query();
438bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  std::vector<std::string> query_params;
448bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  Tokenize(query, "&", &query_params);
458bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  const net::UnescapeRule::Type kUnescapeAll =
468bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      net::UnescapeRule::NORMAL |
478bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      net::UnescapeRule::SPACES |
488bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      net::UnescapeRule::URL_SPECIAL_CHARS |
498bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      net::UnescapeRule::REPLACE_PLUS_WITH_SPACE;
508bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  for (size_t i = 0; i < query_params.size(); ++i) {
518bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    const std::string query_param = query_params[i];
528bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    std::vector<std::string> param_parts;
538bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    Tokenize(query_param, "=", &param_parts);
548bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    if (param_parts.size() != 2)
558bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      continue;
568bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    std::string param_key = net::UnescapeURLComponent(param_parts[0],
578bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)                                                      kUnescapeAll);
588bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    std::string param_value = net::UnescapeURLComponent(param_parts[1],
598bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)                                                        kUnescapeAll);
608bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    if (param_key == "lang") {
618bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      request_language = param_value;
628bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    } else if (param_key == "lm") {
638bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      request_grammar = param_value;
648bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    }
658bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  }
668bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
678bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  delegate_->OnClientConnected();
688bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)}
698bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
708bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)void MockGoogleStreamingServer::OnChunkUpload(int fetcher_id) {
718bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  if (fetcher_id != kUpstreamUrlFetcherId)
728bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      return;
738bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  delegate_->OnClientAudioUpload();
748bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  if (GetURLFetcher(false)->did_receive_last_chunk())
758bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    delegate_->OnClientAudioUploadComplete();
768bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)}
778bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
788bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)void MockGoogleStreamingServer::OnRequestEnd(int fetcher_id) {
798bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  if (fetcher_id != kDownstreamUrlFetcherId)
808bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    return;
818bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  url_fetcher_factory_.RemoveFetcherFromMap(kDownstreamUrlFetcherId);
828bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  delegate_->OnClientDisconnected();
838bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)}
848bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
858bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)void MockGoogleStreamingServer::SimulateResult(
868bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    const SpeechRecognitionResult& result) {
878bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  proto::SpeechRecognitionEvent proto_event;
888bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  proto_event.set_status(proto::SpeechRecognitionEvent::STATUS_SUCCESS);
898bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  proto::SpeechRecognitionResult* proto_result = proto_event.add_result();
908bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  proto_result->set_final(!result.is_provisional);
918bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  for (size_t i = 0; i < result.hypotheses.size(); ++i) {
928bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    proto::SpeechRecognitionAlternative* proto_alternative =
938bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)        proto_result->add_alternative();
948bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    const SpeechRecognitionHypothesis& hypothesis = result.hypotheses[i];
958bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    proto_alternative->set_confidence(hypothesis.confidence);
965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    proto_alternative->set_transcript(base::UTF16ToUTF8(hypothesis.utterance));
978bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  }
988bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
998bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  std::string msg_string;
1008bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  proto_event.SerializeToString(&msg_string);
1018bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
1028bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // Prepend 4 byte prefix length indication to the protobuf message as
1038bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // envisaged by the google streaming recognition webservice protocol.
1045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  uint32 prefix = HostToNet32(checked_cast<uint32>(msg_string.size()));
1058bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  msg_string.insert(0, reinterpret_cast<char*>(&prefix), sizeof(prefix));
1068bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
1078bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  SimulateServerResponse(true, msg_string);
1088bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)}
1098bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
1108bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)void MockGoogleStreamingServer::SimulateServerFailure() {
1118bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  SimulateServerResponse(false, "");
1128bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)}
1138bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
1148bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)void MockGoogleStreamingServer::SimulateMalformedResponse() {
1158bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  std::string json =
1168bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      "{\"status\":0,\"hypotheses\":""[{\"unknownkey\":\"hello\"}]}";
1178bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  SimulateServerResponse(true, json);
1188bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)}
1198bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
1208bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)const std::string& MockGoogleStreamingServer::GetRequestLanguage() const {
1218bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  return request_language;
1228bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)}
1238bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
1248bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)const std::string& MockGoogleStreamingServer::GetRequestGrammar() const {
1258bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  return request_grammar;
1268bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)}
1278bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
1288bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)void MockGoogleStreamingServer::SimulateServerResponse(
1298bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    bool success, const std::string& http_response) {
1308bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  net::TestURLFetcher* fetcher = GetURLFetcher(true);
1318bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
1328bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  net::URLRequestStatus status;
1338bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  status.set_status(success ? net::URLRequestStatus::SUCCESS :
1348bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)                              net::URLRequestStatus::FAILED);
1358bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  fetcher->set_status(status);
1368bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  fetcher->set_response_code(success ? 200 : 500);
1378bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  fetcher->SetResponseString(http_response);
1388bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  fetcher->delegate()->OnURLFetchDownloadProgress(fetcher, 0, 0);
1398bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)}
1408bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
1418bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)// Can return NULL if the SpeechRecognizer has not requested the connection yet.
1428bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)net::TestURLFetcher* MockGoogleStreamingServer::GetURLFetcher(
1438bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    bool downstream) const {
1448bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  return url_fetcher_factory_.GetFetcherByID(
1458bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      downstream ? kDownstreamUrlFetcherId : kUpstreamUrlFetcherId);
1468bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)}
1478bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
1488bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)}  // namespace content
149