blob: 9823947fb5dfdff312ce49cf46ba41ccdb3212af [file] [log] [blame]
// Copyright (c) 2012 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 "net/test/embedded_test_server/embedded_test_server.h"
#include "base/strings/stringprintf.h"
#include "base/threading/thread.h"
#include "net/http/http_response_headers.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "net/url_request/url_fetcher.h"
#include "net/url_request/url_fetcher_delegate.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
namespace test_server {
namespace {
// Gets the content from the given URLFetcher.
std::string GetContentFromFetcher(const URLFetcher& fetcher) {
std::string result;
const bool success = fetcher.GetResponseAsString(&result);
EXPECT_TRUE(success);
return result;
}
// Gets the content type from the given URLFetcher.
std::string GetContentTypeFromFetcher(const URLFetcher& fetcher) {
const HttpResponseHeaders* headers = fetcher.GetResponseHeaders();
if (headers) {
std::string content_type;
if (headers->GetMimeType(&content_type))
return content_type;
}
return std::string();
}
} // namespace
class EmbeddedTestServerTest: public testing::Test,
public URLFetcherDelegate {
public:
EmbeddedTestServerTest()
: num_responses_received_(0),
num_responses_expected_(0),
io_thread_("io_thread") {
}
virtual void SetUp() OVERRIDE {
base::Thread::Options thread_options;
thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
ASSERT_TRUE(io_thread_.StartWithOptions(thread_options));
request_context_getter_ = new TestURLRequestContextGetter(
io_thread_.message_loop_proxy());
server_.reset(new EmbeddedTestServer);
ASSERT_TRUE(server_->InitializeAndWaitUntilReady());
}
virtual void TearDown() OVERRIDE {
ASSERT_TRUE(server_->ShutdownAndWaitUntilComplete());
}
// URLFetcherDelegate override.
virtual void OnURLFetchComplete(const URLFetcher* source) OVERRIDE {
++num_responses_received_;
if (num_responses_received_ == num_responses_expected_)
base::MessageLoop::current()->Quit();
}
// Waits until the specified number of responses are received.
void WaitForResponses(int num_responses) {
num_responses_received_ = 0;
num_responses_expected_ = num_responses;
// Will be terminated in OnURLFetchComplete().
base::MessageLoop::current()->Run();
}
// Handles |request| sent to |path| and returns the response per |content|,
// |content type|, and |code|. Saves the request URL for verification.
scoped_ptr<HttpResponse> HandleRequest(const std::string& path,
const std::string& content,
const std::string& content_type,
HttpStatusCode code,
const HttpRequest& request) {
request_relative_url_ = request.relative_url;
GURL absolute_url = server_->GetURL(request.relative_url);
if (absolute_url.path() == path) {
scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
http_response->set_code(code);
http_response->set_content(content);
http_response->set_content_type(content_type);
return http_response.PassAs<HttpResponse>();
}
return scoped_ptr<HttpResponse>();
}
protected:
int num_responses_received_;
int num_responses_expected_;
std::string request_relative_url_;
base::Thread io_thread_;
scoped_refptr<TestURLRequestContextGetter> request_context_getter_;
scoped_ptr<EmbeddedTestServer> server_;
};
TEST_F(EmbeddedTestServerTest, GetBaseURL) {
EXPECT_EQ(base::StringPrintf("http://127.0.0.1:%d/", server_->port()),
server_->base_url().spec());
}
TEST_F(EmbeddedTestServerTest, GetURL) {
EXPECT_EQ(base::StringPrintf("http://127.0.0.1:%d/path?query=foo",
server_->port()),
server_->GetURL("/path?query=foo").spec());
}
TEST_F(EmbeddedTestServerTest, RegisterRequestHandler) {
server_->RegisterRequestHandler(
base::Bind(&EmbeddedTestServerTest::HandleRequest,
base::Unretained(this),
"/test",
"<b>Worked!</b>",
"text/html",
HTTP_OK));
scoped_ptr<URLFetcher> fetcher(
URLFetcher::Create(server_->GetURL("/test?q=foo"),
URLFetcher::GET,
this));
fetcher->SetRequestContext(request_context_getter_.get());
fetcher->Start();
WaitForResponses(1);
EXPECT_EQ(URLRequestStatus::SUCCESS, fetcher->GetStatus().status());
EXPECT_EQ(HTTP_OK, fetcher->GetResponseCode());
EXPECT_EQ("<b>Worked!</b>", GetContentFromFetcher(*fetcher));
EXPECT_EQ("text/html", GetContentTypeFromFetcher(*fetcher));
EXPECT_EQ("/test?q=foo", request_relative_url_);
}
TEST_F(EmbeddedTestServerTest, ServeFilesFromDirectory) {
base::FilePath src_dir;
ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &src_dir));
server_->ServeFilesFromDirectory(
src_dir.AppendASCII("net").AppendASCII("data"));
scoped_ptr<URLFetcher> fetcher(
URLFetcher::Create(server_->GetURL("/test.html"),
URLFetcher::GET,
this));
fetcher->SetRequestContext(request_context_getter_.get());
fetcher->Start();
WaitForResponses(1);
EXPECT_EQ(URLRequestStatus::SUCCESS, fetcher->GetStatus().status());
EXPECT_EQ(HTTP_OK, fetcher->GetResponseCode());
EXPECT_EQ("<p>Hello World!</p>", GetContentFromFetcher(*fetcher));
EXPECT_EQ("", GetContentTypeFromFetcher(*fetcher));
}
TEST_F(EmbeddedTestServerTest, DefaultNotFoundResponse) {
scoped_ptr<URLFetcher> fetcher(
URLFetcher::Create(server_->GetURL("/non-existent"),
URLFetcher::GET,
this));
fetcher->SetRequestContext(request_context_getter_.get());
fetcher->Start();
WaitForResponses(1);
EXPECT_EQ(URLRequestStatus::SUCCESS, fetcher->GetStatus().status());
EXPECT_EQ(HTTP_NOT_FOUND, fetcher->GetResponseCode());
}
TEST_F(EmbeddedTestServerTest, ConcurrentFetches) {
server_->RegisterRequestHandler(
base::Bind(&EmbeddedTestServerTest::HandleRequest,
base::Unretained(this),
"/test1",
"Raspberry chocolate",
"text/html",
HTTP_OK));
server_->RegisterRequestHandler(
base::Bind(&EmbeddedTestServerTest::HandleRequest,
base::Unretained(this),
"/test2",
"Vanilla chocolate",
"text/html",
HTTP_OK));
server_->RegisterRequestHandler(
base::Bind(&EmbeddedTestServerTest::HandleRequest,
base::Unretained(this),
"/test3",
"No chocolates",
"text/plain",
HTTP_NOT_FOUND));
scoped_ptr<URLFetcher> fetcher1 = scoped_ptr<URLFetcher>(
URLFetcher::Create(server_->GetURL("/test1"),
URLFetcher::GET,
this));
fetcher1->SetRequestContext(request_context_getter_.get());
scoped_ptr<URLFetcher> fetcher2 = scoped_ptr<URLFetcher>(
URLFetcher::Create(server_->GetURL("/test2"),
URLFetcher::GET,
this));
fetcher2->SetRequestContext(request_context_getter_.get());
scoped_ptr<URLFetcher> fetcher3 = scoped_ptr<URLFetcher>(
URLFetcher::Create(server_->GetURL("/test3"),
URLFetcher::GET,
this));
fetcher3->SetRequestContext(request_context_getter_.get());
// Fetch the three URLs concurrently.
fetcher1->Start();
fetcher2->Start();
fetcher3->Start();
WaitForResponses(3);
EXPECT_EQ(URLRequestStatus::SUCCESS, fetcher1->GetStatus().status());
EXPECT_EQ(HTTP_OK, fetcher1->GetResponseCode());
EXPECT_EQ("Raspberry chocolate", GetContentFromFetcher(*fetcher1));
EXPECT_EQ("text/html", GetContentTypeFromFetcher(*fetcher1));
EXPECT_EQ(URLRequestStatus::SUCCESS, fetcher2->GetStatus().status());
EXPECT_EQ(HTTP_OK, fetcher2->GetResponseCode());
EXPECT_EQ("Vanilla chocolate", GetContentFromFetcher(*fetcher2));
EXPECT_EQ("text/html", GetContentTypeFromFetcher(*fetcher2));
EXPECT_EQ(URLRequestStatus::SUCCESS, fetcher3->GetStatus().status());
EXPECT_EQ(HTTP_NOT_FOUND, fetcher3->GetResponseCode());
EXPECT_EQ("No chocolates", GetContentFromFetcher(*fetcher3));
EXPECT_EQ("text/plain", GetContentTypeFromFetcher(*fetcher3));
}
// Below test exercises EmbeddedTestServer's ability to cope with the situation
// where there is no MessageLoop available on the thread at EmbeddedTestServer
// initialization and/or destruction.
typedef std::tr1::tuple<bool, bool> ThreadingTestParams;
class EmbeddedTestServerThreadingTest
: public testing::TestWithParam<ThreadingTestParams> {};
class EmbeddedTestServerThreadingTestDelegate
: public base::PlatformThread::Delegate,
public URLFetcherDelegate {
public:
EmbeddedTestServerThreadingTestDelegate(
bool message_loop_present_on_initialize,
bool message_loop_present_on_shutdown)
: message_loop_present_on_initialize_(message_loop_present_on_initialize),
message_loop_present_on_shutdown_(message_loop_present_on_shutdown) {}
// base::PlatformThread::Delegate:
virtual void ThreadMain() OVERRIDE {
scoped_refptr<base::SingleThreadTaskRunner> io_thread_runner;
base::Thread io_thread("io_thread");
base::Thread::Options thread_options;
thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
ASSERT_TRUE(io_thread.StartWithOptions(thread_options));
io_thread_runner = io_thread.message_loop_proxy();
scoped_ptr<base::MessageLoop> loop;
if (message_loop_present_on_initialize_)
loop.reset(new base::MessageLoopForIO);
// Create the test server instance.
EmbeddedTestServer server;
base::FilePath src_dir;
ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &src_dir));
ASSERT_TRUE(server.InitializeAndWaitUntilReady());
// Make a request and wait for the reply.
if (!loop)
loop.reset(new base::MessageLoopForIO);
scoped_ptr<URLFetcher> fetcher(URLFetcher::Create(
server.GetURL("/test?q=foo"), URLFetcher::GET, this));
fetcher->SetRequestContext(
new TestURLRequestContextGetter(loop->message_loop_proxy()));
fetcher->Start();
loop->Run();
fetcher.reset();
// Shut down.
if (message_loop_present_on_shutdown_)
loop.reset();
ASSERT_TRUE(server.ShutdownAndWaitUntilComplete());
}
// URLFetcherDelegate override.
virtual void OnURLFetchComplete(const URLFetcher* source) OVERRIDE {
base::MessageLoop::current()->Quit();
}
private:
bool message_loop_present_on_initialize_;
bool message_loop_present_on_shutdown_;
DISALLOW_COPY_AND_ASSIGN(EmbeddedTestServerThreadingTestDelegate);
};
TEST_P(EmbeddedTestServerThreadingTest, RunTest) {
// The actual test runs on a separate thread so it can screw with the presence
// of a MessageLoop - the test suite already sets up a MessageLoop for the
// main test thread.
base::PlatformThreadHandle thread_handle;
EmbeddedTestServerThreadingTestDelegate delegate(
std::tr1::get<0>(GetParam()),
std::tr1::get<1>(GetParam()));
ASSERT_TRUE(base::PlatformThread::Create(0, &delegate, &thread_handle));
base::PlatformThread::Join(thread_handle);
}
INSTANTIATE_TEST_CASE_P(EmbeddedTestServerThreadingTestInstantiation,
EmbeddedTestServerThreadingTest,
testing::Combine(testing::Bool(), testing::Bool()));
} // namespace test_server
} // namespace net