1// Copyright 2013 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/test/mock_google_streaming_server.h"
6
7#include "base/bind.h"
8#include "base/numerics/safe_conversions.h"
9#include "base/strings/string_util.h"
10#include "base/strings/utf_string_conversions.h"
11#include "base/sys_byteorder.h"
12#include "base/values.h"
13#include "content/browser/speech/google_streaming_remote_engine.h"
14#include "content/browser/speech/proto/google_streaming_api.pb.h"
15#include "content/browser/speech/speech_recognition_manager_impl.h"
16#include "net/base/escape.h"
17#include "net/url_request/url_fetcher_delegate.h"
18#include "net/url_request/url_request_status.h"
19
20using base::HostToNet32;
21using base::checked_cast;
22
23namespace content {
24
25MockGoogleStreamingServer::MockGoogleStreamingServer(Delegate* delegate)
26    : delegate_(delegate),
27      kDownstreamUrlFetcherId(
28          GoogleStreamingRemoteEngine::kDownstreamUrlFetcherIdForTesting),
29      kUpstreamUrlFetcherId(
30          GoogleStreamingRemoteEngine::kUpstreamUrlFetcherIdForTesting) {
31  url_fetcher_factory_.SetDelegateForTests(this);
32}
33
34MockGoogleStreamingServer::~MockGoogleStreamingServer() {
35}
36
37void MockGoogleStreamingServer::OnRequestStart(int fetcher_id) {
38  if (fetcher_id != kDownstreamUrlFetcherId)
39    return;
40
41  // Extract request argument from the the request URI.
42  std::string query = GetURLFetcher(true)->GetOriginalURL().query();
43  std::vector<std::string> query_params;
44  Tokenize(query, "&", &query_params);
45  const net::UnescapeRule::Type kUnescapeAll =
46      net::UnescapeRule::NORMAL |
47      net::UnescapeRule::SPACES |
48      net::UnescapeRule::URL_SPECIAL_CHARS |
49      net::UnescapeRule::REPLACE_PLUS_WITH_SPACE;
50  for (size_t i = 0; i < query_params.size(); ++i) {
51    const std::string query_param = query_params[i];
52    std::vector<std::string> param_parts;
53    Tokenize(query_param, "=", &param_parts);
54    if (param_parts.size() != 2)
55      continue;
56    std::string param_key = net::UnescapeURLComponent(param_parts[0],
57                                                      kUnescapeAll);
58    std::string param_value = net::UnescapeURLComponent(param_parts[1],
59                                                        kUnescapeAll);
60    if (param_key == "lang") {
61      request_language = param_value;
62    } else if (param_key == "lm") {
63      request_grammar = param_value;
64    }
65  }
66
67  delegate_->OnClientConnected();
68}
69
70void MockGoogleStreamingServer::OnChunkUpload(int fetcher_id) {
71  if (fetcher_id != kUpstreamUrlFetcherId)
72      return;
73  delegate_->OnClientAudioUpload();
74  if (GetURLFetcher(false)->did_receive_last_chunk())
75    delegate_->OnClientAudioUploadComplete();
76}
77
78void MockGoogleStreamingServer::OnRequestEnd(int fetcher_id) {
79  if (fetcher_id != kDownstreamUrlFetcherId)
80    return;
81  url_fetcher_factory_.RemoveFetcherFromMap(kDownstreamUrlFetcherId);
82  delegate_->OnClientDisconnected();
83}
84
85void MockGoogleStreamingServer::SimulateResult(
86    const SpeechRecognitionResult& result) {
87  proto::SpeechRecognitionEvent proto_event;
88  proto_event.set_status(proto::SpeechRecognitionEvent::STATUS_SUCCESS);
89  proto::SpeechRecognitionResult* proto_result = proto_event.add_result();
90  proto_result->set_final(!result.is_provisional);
91  for (size_t i = 0; i < result.hypotheses.size(); ++i) {
92    proto::SpeechRecognitionAlternative* proto_alternative =
93        proto_result->add_alternative();
94    const SpeechRecognitionHypothesis& hypothesis = result.hypotheses[i];
95    proto_alternative->set_confidence(hypothesis.confidence);
96    proto_alternative->set_transcript(base::UTF16ToUTF8(hypothesis.utterance));
97  }
98
99  std::string msg_string;
100  proto_event.SerializeToString(&msg_string);
101
102  // Prepend 4 byte prefix length indication to the protobuf message as
103  // envisaged by the google streaming recognition webservice protocol.
104  uint32 prefix = HostToNet32(checked_cast<uint32>(msg_string.size()));
105  msg_string.insert(0, reinterpret_cast<char*>(&prefix), sizeof(prefix));
106
107  SimulateServerResponse(true, msg_string);
108}
109
110void MockGoogleStreamingServer::SimulateServerFailure() {
111  SimulateServerResponse(false, "");
112}
113
114void MockGoogleStreamingServer::SimulateMalformedResponse() {
115  std::string json =
116      "{\"status\":0,\"hypotheses\":""[{\"unknownkey\":\"hello\"}]}";
117  SimulateServerResponse(true, json);
118}
119
120const std::string& MockGoogleStreamingServer::GetRequestLanguage() const {
121  return request_language;
122}
123
124const std::string& MockGoogleStreamingServer::GetRequestGrammar() const {
125  return request_grammar;
126}
127
128void MockGoogleStreamingServer::SimulateServerResponse(
129    bool success, const std::string& http_response) {
130  net::TestURLFetcher* fetcher = GetURLFetcher(true);
131
132  net::URLRequestStatus status;
133  status.set_status(success ? net::URLRequestStatus::SUCCESS :
134                              net::URLRequestStatus::FAILED);
135  fetcher->set_status(status);
136  fetcher->set_response_code(success ? 200 : 500);
137  fetcher->SetResponseString(http_response);
138  fetcher->delegate()->OnURLFetchDownloadProgress(fetcher, 0, 0);
139}
140
141// Can return NULL if the SpeechRecognizer has not requested the connection yet.
142net::TestURLFetcher* MockGoogleStreamingServer::GetURLFetcher(
143    bool downstream) const {
144  return url_fetcher_factory_.GetFetcherByID(
145      downstream ? kDownstreamUrlFetcherId : kUpstreamUrlFetcherId);
146}
147
148}  // namespace content
149