// 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 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 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(); } return scoped_ptr(); } protected: int num_responses_received_; int num_responses_expected_; std::string request_relative_url_; base::Thread io_thread_; scoped_refptr request_context_getter_; scoped_ptr 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", "Worked!", "text/html", HTTP_OK)); scoped_ptr 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("Worked!", 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 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("

Hello World!

", GetContentFromFetcher(*fetcher)); EXPECT_EQ("", GetContentTypeFromFetcher(*fetcher)); } TEST_F(EmbeddedTestServerTest, DefaultNotFoundResponse) { scoped_ptr 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 fetcher1 = scoped_ptr( URLFetcher::Create(server_->GetURL("/test1"), URLFetcher::GET, this)); fetcher1->SetRequestContext(request_context_getter_.get()); scoped_ptr fetcher2 = scoped_ptr( URLFetcher::Create(server_->GetURL("/test2"), URLFetcher::GET, this)); fetcher2->SetRequestContext(request_context_getter_.get()); scoped_ptr fetcher3 = scoped_ptr( 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 ThreadingTestParams; class EmbeddedTestServerThreadingTest : public testing::TestWithParam {}; 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 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 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 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