Commit b5ce7dd1 authored by Matt Menke's avatar Matt Menke Committed by Commit Bot

Make SpeechRecognitionBrowserTest.OneShotRecognition use the test server

Using the EmbeddedTestServer will make the same test pass before/after
the underlying code is switched to use the network service. The new
test is also more integrationy, using real network requests, which
seems particularly useful here, as this is the only code that sends
chunked uploads in Chrome. The one downside is that it requires a
test-only method to set the server URL.

Bug: 810555
Change-Id: I46b9321f83027cbc1c3a002ce1b35b693a6560aa
Reviewed-on: https://chromium-review.googlesource.com/987526
Commit-Queue: Matt Menke <mmenke@chromium.org>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Reviewed-by: default avatarMax Morin <maxmorin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#548328}
parent 0f0ca6ab
......@@ -11,11 +11,15 @@
#include "base/bind.h"
#include "base/location.h"
#include "base/numerics/safe_conversions.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/sys_byteorder.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "content/browser/speech/proto/google_streaming_api.pb.h"
#include "content/browser/speech/speech_recognition_engine.h"
#include "content/browser/speech/speech_recognition_manager_impl.h"
#include "content/browser/speech/speech_recognizer_impl.h"
......@@ -25,58 +29,66 @@
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/public/test/test_utils.h"
#include "content/shell/browser/shell.h"
#include "content/test/mock_google_streaming_server.h"
#include "media/audio/audio_system_impl.h"
#include "media/audio/audio_thread_impl.h"
#include "media/audio/mock_audio_manager.h"
#include "media/audio/test_audio_input_controller_factory.h"
#include "net/test/embedded_test_server/controllable_http_response.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::RunLoop;
namespace content {
class SpeechRecognitionBrowserTest :
public ContentBrowserTest,
public MockGoogleStreamingServer::Delegate,
public media::TestAudioInputControllerDelegate {
namespace {
std::string MakeGoodResponse() {
proto::SpeechRecognitionEvent proto_event;
proto_event.set_status(proto::SpeechRecognitionEvent::STATUS_SUCCESS);
proto::SpeechRecognitionResult* proto_result = proto_event.add_result();
SpeechRecognitionResult result;
result.hypotheses.push_back(SpeechRecognitionHypothesis(
base::UTF8ToUTF16("Pictures of the moon"), 1.0F));
proto_result->set_final(!result.is_provisional);
for (size_t i = 0; i < result.hypotheses.size(); ++i) {
proto::SpeechRecognitionAlternative* proto_alternative =
proto_result->add_alternative();
const SpeechRecognitionHypothesis& hypothesis = result.hypotheses[i];
proto_alternative->set_confidence(hypothesis.confidence);
proto_alternative->set_transcript(base::UTF16ToUTF8(hypothesis.utterance));
}
std::string msg_string;
proto_event.SerializeToString(&msg_string);
// Prepend 4 byte prefix length indication to the protobuf message as
// envisaged by the google streaming recognition webservice protocol.
uint32_t prefix =
base::HostToNet32(base::checked_cast<uint32_t>(msg_string.size()));
msg_string.insert(0, reinterpret_cast<char*>(&prefix), sizeof(prefix));
return msg_string;
}
} // namespace
class SpeechRecognitionBrowserTest
: public ContentBrowserTest,
public media::TestAudioInputControllerDelegate {
public:
enum StreamingServerState {
kIdle,
kTestAudioControllerOpened,
kClientConnected,
kClientAudioUpload,
kClientAudioUploadComplete,
kTestAudioControllerClosed,
kClientDisconnected
};
// MockGoogleStreamingServerDelegate methods.
void OnClientConnected() override {
ASSERT_EQ(kTestAudioControllerOpened, streaming_server_state_);
streaming_server_state_ = kClientConnected;
}
void OnClientAudioUpload() override {
if (streaming_server_state_ == kClientConnected)
streaming_server_state_ = kClientAudioUpload;
}
void OnClientAudioUploadComplete() override {
ASSERT_EQ(kTestAudioControllerClosed, streaming_server_state_);
streaming_server_state_ = kClientAudioUploadComplete;
}
void OnClientDisconnected() override {
ASSERT_EQ(kClientAudioUploadComplete, streaming_server_state_);
streaming_server_state_ = kClientDisconnected;
}
// media::TestAudioInputControllerDelegate methods.
void TestAudioControllerOpened(
media::TestAudioInputController* controller) override {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
ASSERT_EQ(kIdle, streaming_server_state_);
streaming_server_state_ = kTestAudioControllerOpened;
const int capture_packet_interval_ms =
......@@ -91,12 +103,18 @@ class SpeechRecognitionBrowserTest :
void TestAudioControllerClosed(
media::TestAudioInputController* controller) override {
ASSERT_EQ(kClientAudioUpload, streaming_server_state_);
DCHECK_CURRENTLY_ON(BrowserThread::IO);
ASSERT_EQ(kTestAudioControllerOpened, streaming_server_state_);
streaming_server_state_ = kTestAudioControllerClosed;
mock_streaming_server_->MockGoogleStreamingServer::SimulateResult(
GetGoodSpeechResult());
content::BrowserThread::PostTask(
content::BrowserThread::UI, FROM_HERE,
base::BindOnce(&SpeechRecognitionBrowserTest::SendResponse,
base::Unretained(this)));
}
void SendResponse() {}
// Helper methods used by test fixtures.
GURL GetTestUrlFromFragment(const std::string& fragment) {
return GURL(GetTestUrl("speech", "web_speech_recognition.html").spec() +
......@@ -117,7 +135,6 @@ class SpeechRecognitionBrowserTest :
test_audio_input_controller_factory_.set_delegate(this);
media::AudioInputController::set_factory_for_testing(
&test_audio_input_controller_factory_);
mock_streaming_server_.reset(new MockGoogleStreamingServer(this));
streaming_server_state_ = kIdle;
ASSERT_TRUE(SpeechRecognitionManagerImpl::GetInstance());
......@@ -139,7 +156,6 @@ class SpeechRecognitionBrowserTest :
audio_manager_->Shutdown();
test_audio_input_controller_factory_.set_delegate(nullptr);
mock_streaming_server_.reset();
}
private:
......@@ -191,17 +207,10 @@ class SpeechRecognitionBrowserTest :
}
}
SpeechRecognitionResult GetGoodSpeechResult() {
SpeechRecognitionResult result;
result.hypotheses.push_back(SpeechRecognitionHypothesis(
base::UTF8ToUTF16("Pictures of the moon"), 1.0F));
return result;
}
std::unique_ptr<media::MockAudioManager> audio_manager_;
std::unique_ptr<media::AudioSystem> audio_system_;
StreamingServerState streaming_server_state_;
std::unique_ptr<MockGoogleStreamingServer> mock_streaming_server_;
media::TestAudioInputControllerFactory test_audio_input_controller_factory_;
};
......@@ -223,11 +232,53 @@ IN_PROC_BROWSER_TEST_F(SpeechRecognitionBrowserTest, DISABLED_Precheck) {
#define MAYBE_OneShotRecognition OneShotRecognition
#endif
IN_PROC_BROWSER_TEST_F(SpeechRecognitionBrowserTest, MAYBE_OneShotRecognition) {
NavigateToURLBlockUntilNavigationsComplete(
shell(), GetTestUrlFromFragment("oneshot"), 2);
// Set up a test server, with two response handlers.
net::test_server::ControllableHttpResponse upstream_response(
embedded_test_server(), "/foo/up?", true /* relative_url_is_prefix */);
net::test_server::ControllableHttpResponse downstream_response(
embedded_test_server(), "/foo/down?", true /* relative_url_is_prefix */);
ASSERT_TRUE(embedded_test_server()->Start());
// Use a base path that doesn't end in a slash to mimic the default URL.
std::string web_service_base_url =
embedded_test_server()->base_url().spec() + "foo";
SpeechRecognitionEngine::set_web_service_base_url_for_tests(
web_service_base_url.c_str());
// Need to watch for two navigations. Can't use
// NavigateToURLBlockUntilNavigationsComplete so that the
// ControllableHttpResponses can be used to wait for the test server to see
// the network requests, and response to them.
TestNavigationObserver navigation_observer(shell()->web_contents(), 2);
shell()->LoadURL(GetTestUrlFromFragment("oneshot"));
// Wait for the upstream HTTP request to be completely received, and return an
// empty response.
upstream_response.WaitForRequest();
EXPECT_FALSE(upstream_response.http_request()->content.empty());
EXPECT_EQ(net::test_server::METHOD_POST,
upstream_response.http_request()->method);
EXPECT_EQ("chunked",
upstream_response.http_request()->headers.at("Transfer-Encoding"));
EXPECT_EQ("audio/x-flac; rate=16000",
upstream_response.http_request()->headers.at("Content-Type"));
upstream_response.Send("HTTP/1.1 200 OK\r\n\r\n");
upstream_response.Done();
EXPECT_EQ(kClientDisconnected, streaming_server_state());
// Wait for the downstream HTTP request to be received, and response with a
// valid response.
downstream_response.WaitForRequest();
EXPECT_EQ(net::test_server::METHOD_GET,
downstream_response.http_request()->method);
downstream_response.Send("HTTP/1.1 200 OK\r\n\r\n" + MakeGoodResponse());
downstream_response.Done();
navigation_observer.Wait();
EXPECT_EQ(kTestAudioControllerClosed, streaming_server_state());
EXPECT_EQ("goodresult1", GetPageFragment());
// Remove reference to URL string that's on the stack.
SpeechRecognitionEngine::set_web_service_base_url_for_tests(nullptr);
}
} // namespace content
......@@ -38,6 +38,9 @@ const char kWebServiceBaseUrl[] =
const char kDownstreamUrl[] = "/down?";
const char kUpstreamUrl[] = "/up?";
// Used to override |kWebServiceBaseUrl| when non-null, only set in tests.
const char* web_service_base_url_for_tests = nullptr;
// This matches the maximum maxAlternatives value supported by the server.
const uint32_t kMaxMaxAlternatives = 30;
......@@ -106,6 +109,11 @@ SpeechRecognitionEngine::~SpeechRecognitionEngine() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void SpeechRecognitionEngine::set_web_service_base_url_for_tests(
const char* base_url_for_tests) {
web_service_base_url_for_tests = base_url_for_tests;
}
void SpeechRecognitionEngine::SetConfig(const Config& config) {
config_ = config;
}
......@@ -330,13 +338,17 @@ SpeechRecognitionEngine::ConnectBothStreams(const FSMEventArgs&) {
config_.preamble->sample_depth * 8));
}
const char* web_service_base_url = !web_service_base_url_for_tests
? kWebServiceBaseUrl
: web_service_base_url_for_tests;
// Setup downstream fetcher.
std::vector<std::string> downstream_args;
downstream_args.push_back(
"key=" + net::EscapeQueryParamValue(google_apis::GetAPIKey(), true));
downstream_args.push_back("pair=" + request_key);
downstream_args.push_back("output=pb");
GURL downstream_url(std::string(kWebServiceBaseUrl) +
GURL downstream_url(std::string(web_service_base_url) +
std::string(kDownstreamUrl) +
base::JoinString(downstream_args, "&"));
......@@ -432,7 +444,8 @@ SpeechRecognitionEngine::ConnectBothStreams(const FSMEventArgs&) {
upstream_args.push_back(
"audioFormat=" + net::EscapeQueryParamValue(audio_format, true));
}
GURL upstream_url(std::string(kWebServiceBaseUrl) +
GURL upstream_url(std::string(web_service_base_url) +
std::string(kUpstreamUrl) +
base::JoinString(upstream_args, "&"));
......
......@@ -104,6 +104,10 @@ class CONTENT_EXPORT SpeechRecognitionEngine : public net::URLFetcherDelegate {
explicit SpeechRecognitionEngine(net::URLRequestContextGetter* context);
~SpeechRecognitionEngine() override;
// Sets the URL requests are sent to for tests.
static void set_web_service_base_url_for_tests(
const char* base_url_for_tests);
void SetConfig(const Config& config);
void StartRecognition();
void EndRecognition();
......
......@@ -198,8 +198,6 @@ jumbo_static_library("test_support") {
"gpu_browsertest_helpers.h",
"mock_background_sync_controller.cc",
"mock_background_sync_controller.h",
"mock_google_streaming_server.cc",
"mock_google_streaming_server.h",
"mock_keyboard.cc",
"mock_keyboard.h",
"mock_keyboard_driver_win.cc",
......@@ -365,10 +363,6 @@ jumbo_static_library("test_support") {
]
if (is_android) {
sources -= [
"mock_google_streaming_server.cc",
"mock_google_streaming_server.h",
]
deps += [
"//content/shell:android_shell_descriptors",
"//mojo/android:libsystem_java",
......@@ -1054,6 +1048,7 @@ test("content_browsertests") {
"../browser/speech/speech_recognition_browsertest.cc",
"../browser/zoom_browsertest.cc",
]
deps += [ "//content/browser/speech/proto" ]
}
# HID support is not available without udev.
......
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/test/mock_google_streaming_server.h"
#include <stddef.h>
#include <stdint.h>
#include "base/bind.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/sys_byteorder.h"
#include "base/values.h"
#include "content/browser/speech/proto/google_streaming_api.pb.h"
#include "content/browser/speech/speech_recognition_engine.h"
#include "content/browser/speech/speech_recognition_manager_impl.h"
#include "net/base/escape.h"
#include "net/base/net_errors.h"
#include "net/url_request/url_fetcher_delegate.h"
#include "net/url_request/url_request_status.h"
using base::HostToNet32;
using base::checked_cast;
namespace content {
MockGoogleStreamingServer::MockGoogleStreamingServer(Delegate* delegate)
: delegate_(delegate),
kDownstreamUrlFetcherId(
SpeechRecognitionEngine::kDownstreamUrlFetcherIdForTesting),
kUpstreamUrlFetcherId(
SpeechRecognitionEngine::kUpstreamUrlFetcherIdForTesting) {
url_fetcher_factory_.SetDelegateForTests(this);
}
MockGoogleStreamingServer::~MockGoogleStreamingServer() {
}
void MockGoogleStreamingServer::OnRequestStart(int fetcher_id) {
if (fetcher_id != kDownstreamUrlFetcherId)
return;
// Extract request argument from the the request URI.
std::string query = GetURLFetcher(true)->GetOriginalURL().query();
const net::UnescapeRule::Type kUnescapeAll =
net::UnescapeRule::NORMAL | net::UnescapeRule::SPACES |
net::UnescapeRule::PATH_SEPARATORS |
net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS |
net::UnescapeRule::REPLACE_PLUS_WITH_SPACE;
for (const base::StringPiece& query_param :
base::SplitStringPiece(query, "&", base::KEEP_WHITESPACE,
base::SPLIT_WANT_NONEMPTY)) {
std::vector<std::string> param_parts = base::SplitString(
query_param, "=", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
if (param_parts.size() != 2)
continue;
std::string param_key = net::UnescapeURLComponent(param_parts[0],
kUnescapeAll);
std::string param_value = net::UnescapeURLComponent(param_parts[1],
kUnescapeAll);
if (param_key == "lang") {
request_language = param_value;
} else if (param_key == "lm") {
request_grammar = param_value;
}
}
delegate_->OnClientConnected();
}
void MockGoogleStreamingServer::OnChunkUpload(int fetcher_id) {
if (fetcher_id != kUpstreamUrlFetcherId)
return;
delegate_->OnClientAudioUpload();
if (GetURLFetcher(false)->did_receive_last_chunk())
delegate_->OnClientAudioUploadComplete();
}
void MockGoogleStreamingServer::OnRequestEnd(int fetcher_id) {
if (fetcher_id != kDownstreamUrlFetcherId)
return;
url_fetcher_factory_.RemoveFetcherFromMap(kDownstreamUrlFetcherId);
delegate_->OnClientDisconnected();
}
void MockGoogleStreamingServer::SimulateResult(
const SpeechRecognitionResult& result) {
proto::SpeechRecognitionEvent proto_event;
proto_event.set_status(proto::SpeechRecognitionEvent::STATUS_SUCCESS);
proto::SpeechRecognitionResult* proto_result = proto_event.add_result();
proto_result->set_final(!result.is_provisional);
for (size_t i = 0; i < result.hypotheses.size(); ++i) {
proto::SpeechRecognitionAlternative* proto_alternative =
proto_result->add_alternative();
const SpeechRecognitionHypothesis& hypothesis = result.hypotheses[i];
proto_alternative->set_confidence(hypothesis.confidence);
proto_alternative->set_transcript(base::UTF16ToUTF8(hypothesis.utterance));
}
std::string msg_string;
proto_event.SerializeToString(&msg_string);
// Prepend 4 byte prefix length indication to the protobuf message as
// envisaged by the google streaming recognition webservice protocol.
uint32_t prefix = HostToNet32(checked_cast<uint32_t>(msg_string.size()));
msg_string.insert(0, reinterpret_cast<char*>(&prefix), sizeof(prefix));
SimulateServerResponse(true, msg_string);
}
void MockGoogleStreamingServer::SimulateServerFailure() {
SimulateServerResponse(false, "");
}
void MockGoogleStreamingServer::SimulateMalformedResponse() {
std::string json =
"{\"status\":0,\"hypotheses\":""[{\"unknownkey\":\"hello\"}]}";
SimulateServerResponse(true, json);
}
const std::string& MockGoogleStreamingServer::GetRequestLanguage() const {
return request_language;
}
const std::string& MockGoogleStreamingServer::GetRequestGrammar() const {
return request_grammar;
}
void MockGoogleStreamingServer::SimulateServerResponse(
bool success, const std::string& http_response) {
net::TestURLFetcher* fetcher = GetURLFetcher(true);
fetcher->set_status(
net::URLRequestStatus::FromError(success ? net::OK : net::ERR_FAILED));
fetcher->set_response_code(success ? 200 : 500);
fetcher->SetResponseString(http_response);
fetcher->delegate()->OnURLFetchDownloadProgress(fetcher, 0, 0, 0);
}
// Can return NULL if the SpeechRecognizer has not requested the connection yet.
net::TestURLFetcher* MockGoogleStreamingServer::GetURLFetcher(
bool downstream) const {
return url_fetcher_factory_.GetFetcherByID(
downstream ? kDownstreamUrlFetcherId : kUpstreamUrlFetcherId);
}
} // namespace content
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_TEST_MOCK_GOOGLE_STREAMING_SERVER_H_
#define CONTENT_TEST_MOCK_GOOGLE_STREAMING_SERVER_H_
#include "base/compiler_specific.h"
#include "base/macros.h"
#include "content/public/common/speech_recognition_result.h"
#include "net/url_request/test_url_fetcher_factory.h"
namespace content {
struct SpeechRecognitionResult;
// Provides a mock implementation of the Google remote streaming speech
// recognition webservice, exploiting the TestURLFetcher to extract request
// parameters and provide forged JSON responses back to the client.
// It is intended for closing the server-side loop in speech tests that involve
// the SpeechRecognitionEngine client.
class MockGoogleStreamingServer : public net::TestURLFetcherDelegateForTests {
public:
class Delegate {
public:
virtual void OnClientConnected() = 0;
virtual void OnClientAudioUpload() = 0;
virtual void OnClientAudioUploadComplete() = 0;
virtual void OnClientDisconnected() = 0;
};
explicit MockGoogleStreamingServer(Delegate* delegate);
virtual ~MockGoogleStreamingServer();
// net::TestURLFetcherDelegateForTests implementation.
void OnRequestStart(int fetcher_id) override;
void OnChunkUpload(int fetcher_id) override;
void OnRequestEnd(int fetcher_id) override;
void SimulateResult(const content::SpeechRecognitionResult& result);
void SimulateServerFailure();
void SimulateMalformedResponse();
// Retrieves the language parmeter for the request (e.g., lang=en-US).
const std::string& GetRequestLanguage() const;
// Retrieves the grammar parmeter for the request (e.g., lm=http://url/g.xml).
const std::string& GetRequestGrammar() const;
private:
void SimulateServerResponse(bool success, const std::string& http_response);
net::TestURLFetcher* GetURLFetcher(bool downstream) const;
Delegate* delegate_;
int kDownstreamUrlFetcherId;
int kUpstreamUrlFetcherId;
net::TestURLFetcherFactory url_fetcher_factory_;
// Request arguments extracted by the HTTP query string.
std::string request_language;
std::string request_grammar;
DISALLOW_COPY_AND_ASSIGN(MockGoogleStreamingServer);
};
} // namespace content
#endif // CONTENT_TEST_MOCK_GOOGLE_STREAMING_SERVER_H_
......@@ -15,9 +15,11 @@ class ControllableHttpResponse::Interceptor : public HttpResponse {
public:
explicit Interceptor(
base::WeakPtr<ControllableHttpResponse> controller,
scoped_refptr<base::SingleThreadTaskRunner> controller_task_runner)
scoped_refptr<base::SingleThreadTaskRunner> controller_task_runner,
const HttpRequest& http_request)
: controller_(controller),
controller_task_runner_(controller_task_runner) {}
controller_task_runner_(controller_task_runner),
http_request_(std::make_unique<HttpRequest>(http_request)) {}
~Interceptor() override {}
private:
......@@ -26,24 +28,28 @@ class ControllableHttpResponse::Interceptor : public HttpResponse {
controller_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&ControllableHttpResponse::OnRequest, controller_,
base::ThreadTaskRunnerHandle::Get(), send, done));
base::ThreadTaskRunnerHandle::Get(), send, done,
std::move(http_request_)));
}
base::WeakPtr<ControllableHttpResponse> controller_;
scoped_refptr<base::SingleThreadTaskRunner> controller_task_runner_;
std::unique_ptr<HttpRequest> http_request_;
DISALLOW_COPY_AND_ASSIGN(Interceptor);
};
ControllableHttpResponse::ControllableHttpResponse(
EmbeddedTestServer* embedded_test_server,
const std::string& relative_url)
const std::string& relative_url,
bool relative_url_is_prefix)
: weak_ptr_factory_(this) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
embedded_test_server->RegisterRequestHandler(
base::BindRepeating(RequestHandler, weak_ptr_factory_.GetWeakPtr(),
base::ThreadTaskRunnerHandle::Get(),
base::Owned(new bool(true)), relative_url));
embedded_test_server->RegisterRequestHandler(base::BindRepeating(
RequestHandler, weak_ptr_factory_.GetWeakPtr(),
base::ThreadTaskRunnerHandle::Get(), base::Owned(new bool(true)),
relative_url, relative_url_is_prefix));
}
ControllableHttpResponse::~ControllableHttpResponse() {}
......@@ -83,13 +89,15 @@ void ControllableHttpResponse::OnRequest(
scoped_refptr<base::SingleThreadTaskRunner>
embedded_test_server_task_runner,
const SendBytesCallback& send,
const SendCompleteCallback& done) {
const SendCompleteCallback& done,
std::unique_ptr<HttpRequest> http_request) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!embedded_test_server_task_runner_)
<< "A ControllableHttpResponse can only handle one request at a time";
embedded_test_server_task_runner_ = embedded_test_server_task_runner;
send_ = send;
done_ = done;
http_request_ = std::move(http_request);
loop_.Quit();
}
......@@ -100,12 +108,20 @@ std::unique_ptr<HttpResponse> ControllableHttpResponse::RequestHandler(
scoped_refptr<base::SingleThreadTaskRunner> controller_task_runner,
bool* available,
const std::string& relative_url,
bool relative_url_is_prefix,
const HttpRequest& request) {
if (*available && request.relative_url == relative_url) {
if (!*available)
return nullptr;
if (request.relative_url == relative_url ||
(relative_url_is_prefix &&
base::StartsWith(request.relative_url, relative_url,
base::CompareCase::SENSITIVE))) {
*available = false;
return std::make_unique<ControllableHttpResponse::Interceptor>(
controller, controller_task_runner);
controller, controller_task_runner, request);
}
return nullptr;
}
......
......@@ -27,10 +27,15 @@ namespace test_server {
// handle only **one** request with the matching |relative_url|. In the case of
// multiple ControllableHttpResponses for the same path, they're used in the
// order they were created.
//
// If |relative_url_is_prefix| is true, |relative_url| is only compared agaisnt
// the start of the URL being requested, which allows matching against (possibly
// variable) query strings, for instance.
class ControllableHttpResponse {
public:
ControllableHttpResponse(EmbeddedTestServer* embedded_test_server,
const std::string& relative_path);
const std::string& relative_url,
bool relative_url_is_prefix = false);
~ControllableHttpResponse();
// These method are intented to be used in order.
......@@ -44,6 +49,9 @@ class ControllableHttpResponse {
// 3) Notify there are no more data to be sent and close the socket.
void Done();
// Returns the HttpRequest after a call to WaitForRequest.
const HttpRequest* http_request() const { return http_request_.get(); }
private:
class Interceptor;
......@@ -52,13 +60,15 @@ class ControllableHttpResponse {
void OnRequest(scoped_refptr<base::SingleThreadTaskRunner>
embedded_test_server_task_runner,
const SendBytesCallback& send,
const SendCompleteCallback& done);
const SendCompleteCallback& done,
std::unique_ptr<HttpRequest> http_request);
static std::unique_ptr<HttpResponse> RequestHandler(
base::WeakPtr<ControllableHttpResponse> controller,
scoped_refptr<base::SingleThreadTaskRunner> controller_task_runner,
bool* available,
const std::string& relative_url,
bool relative_url_is_prefix,
const HttpRequest& request);
State state_ = State::WAITING_FOR_REQUEST;
......@@ -66,6 +76,8 @@ class ControllableHttpResponse {
scoped_refptr<base::SingleThreadTaskRunner> embedded_test_server_task_runner_;
SendBytesCallback send_;
SendCompleteCallback done_;
std::unique_ptr<HttpRequest> http_request_;
SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<ControllableHttpResponse> weak_ptr_factory_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment