blob: 7d7c657a50895147adb04b17279140ff3ab95649 [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 "base/command_line.h"
#include "base/location.h"
#include "base/memory/ref_counted.h"
#include "base/single_thread_task_runner.h"
#include "base/synchronization/waitable_event.h"
#include "base/thread_task_runner_handle.h"
#include "base/threading/thread.h"
#include "base/values.h"
#include "chrome/service/cloud_print/cloud_print_url_fetcher.h"
#include "chrome/service/service_process.h"
#include "net/test/spawned_test_server/spawned_test_server.h"
#include "net/url_request/url_request_context_getter.h"
#include "net/url_request/url_request_status.h"
#include "net/url_request/url_request_test_util.h"
#include "net/url_request/url_request_throttler_manager.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
using base::Time;
using base::TimeDelta;
namespace cloud_print {
const base::FilePath::CharType kDocRoot[] =
FILE_PATH_LITERAL("chrome/test/data");
int g_request_context_getter_instances = 0;
class TrackingTestURLRequestContextGetter
: public net::TestURLRequestContextGetter {
public:
explicit TrackingTestURLRequestContextGetter(
base::SingleThreadTaskRunner* io_task_runner,
net::URLRequestThrottlerManager* throttler_manager)
: TestURLRequestContextGetter(io_task_runner),
throttler_manager_(throttler_manager) {
g_request_context_getter_instances++;
}
net::TestURLRequestContext* GetURLRequestContext() override {
if (!context_.get()) {
context_.reset(new net::TestURLRequestContext(true));
context_->set_throttler_manager(throttler_manager_);
context_->Init();
}
return context_.get();
}
protected:
~TrackingTestURLRequestContextGetter() override {
g_request_context_getter_instances--;
}
private:
// Not owned here.
net::URLRequestThrottlerManager* throttler_manager_;
scoped_ptr<net::TestURLRequestContext> context_;
};
class TestCloudPrintURLFetcher : public CloudPrintURLFetcher {
public:
explicit TestCloudPrintURLFetcher(
base::SingleThreadTaskRunner* io_task_runner)
: io_task_runner_(io_task_runner) {}
net::URLRequestContextGetter* GetRequestContextGetter() override {
return new TrackingTestURLRequestContextGetter(io_task_runner_.get(),
throttler_manager());
}
net::URLRequestThrottlerManager* throttler_manager() {
return &throttler_manager_;
}
private:
~TestCloudPrintURLFetcher() override {}
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
// We set this as the throttler manager for the
// TestURLRequestContext we create.
net::URLRequestThrottlerManager throttler_manager_;
};
class CloudPrintURLFetcherTest : public testing::Test,
public CloudPrintURLFetcherDelegate {
public:
CloudPrintURLFetcherTest() : max_retries_(0), fetcher_(NULL) { }
// Creates a URLFetcher, using the program's main thread to do IO.
virtual void CreateFetcher(const GURL& url, int max_retries);
// CloudPrintURLFetcher::Delegate
CloudPrintURLFetcher::ResponseAction HandleRawResponse(
const net::URLFetcher* source,
const GURL& url,
const net::URLRequestStatus& status,
int response_code,
const net::ResponseCookies& cookies,
const std::string& data) override;
CloudPrintURLFetcher::ResponseAction OnRequestAuthError() override {
ADD_FAILURE();
return CloudPrintURLFetcher::STOP_PROCESSING;
}
std::string GetAuthHeader() override { return std::string(); }
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner() {
return io_task_runner_;
}
protected:
void SetUp() override {
testing::Test::SetUp();
io_task_runner_ = base::ThreadTaskRunnerHandle::Get();
}
void TearDown() override {
fetcher_ = NULL;
// Deleting the fetcher causes a task to be posted to the IO thread to
// release references to the URLRequestContextGetter. We need to run all
// pending tasks to execute that (this is the IO thread).
base::MessageLoop::current()->RunUntilIdle();
EXPECT_EQ(0, g_request_context_getter_instances);
}
// URLFetcher is designed to run on the main UI thread, but in our tests
// we assume that the current thread is the IO thread where the URLFetcher
// dispatches its requests to. When we wish to simulate being used from
// a UI thread, we dispatch a worker thread to do so.
base::MessageLoopForIO io_loop_;
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
int max_retries_;
Time start_time_;
scoped_refptr<TestCloudPrintURLFetcher> fetcher_;
};
class CloudPrintURLFetcherBasicTest : public CloudPrintURLFetcherTest {
public:
CloudPrintURLFetcherBasicTest()
: handle_raw_response_(false), handle_raw_data_(false) { }
// CloudPrintURLFetcher::Delegate
CloudPrintURLFetcher::ResponseAction HandleRawResponse(
const net::URLFetcher* source,
const GURL& url,
const net::URLRequestStatus& status,
int response_code,
const net::ResponseCookies& cookies,
const std::string& data) override;
CloudPrintURLFetcher::ResponseAction HandleRawData(
const net::URLFetcher* source,
const GURL& url,
const std::string& data) override;
CloudPrintURLFetcher::ResponseAction HandleJSONData(
const net::URLFetcher* source,
const GURL& url,
base::DictionaryValue* json_data,
bool succeeded) override;
void SetHandleRawResponse(bool handle_raw_response) {
handle_raw_response_ = handle_raw_response;
}
void SetHandleRawData(bool handle_raw_data) {
handle_raw_data_ = handle_raw_data;
}
private:
bool handle_raw_response_;
bool handle_raw_data_;
};
// Version of CloudPrintURLFetcherTest that tests overload protection.
class CloudPrintURLFetcherOverloadTest : public CloudPrintURLFetcherTest {
public:
CloudPrintURLFetcherOverloadTest() : response_count_(0) {
}
// CloudPrintURLFetcher::Delegate
CloudPrintURLFetcher::ResponseAction HandleRawData(
const net::URLFetcher* source,
const GURL& url,
const std::string& data) override;
private:
int response_count_;
};
// Version of CloudPrintURLFetcherTest that tests backoff protection.
class CloudPrintURLFetcherRetryBackoffTest : public CloudPrintURLFetcherTest {
public:
CloudPrintURLFetcherRetryBackoffTest() : response_count_(0) {
}
// CloudPrintURLFetcher::Delegate
CloudPrintURLFetcher::ResponseAction HandleRawData(
const net::URLFetcher* source,
const GURL& url,
const std::string& data) override;
void OnRequestGiveUp() override;
private:
int response_count_;
};
void CloudPrintURLFetcherTest::CreateFetcher(const GURL& url, int max_retries) {
fetcher_ = new TestCloudPrintURLFetcher(io_task_runner().get());
// Registers an entry for test url. It only allows 3 requests to be sent
// in 200 milliseconds.
scoped_refptr<net::URLRequestThrottlerEntry>
entry(new net::URLRequestThrottlerEntry(
fetcher_->throttler_manager(), std::string(), 200, 3, 1, 2.0, 0.0, 256));
fetcher_->throttler_manager()->OverrideEntryForTests(url, entry.get());
max_retries_ = max_retries;
start_time_ = Time::Now();
fetcher_->StartGetRequest(CloudPrintURLFetcher::REQUEST_MAX, url, this,
max_retries_, std::string());
}
CloudPrintURLFetcher::ResponseAction
CloudPrintURLFetcherTest::HandleRawResponse(
const net::URLFetcher* source,
const GURL& url,
const net::URLRequestStatus& status,
int response_code,
const net::ResponseCookies& cookies,
const std::string& data) {
EXPECT_TRUE(status.is_success());
EXPECT_EQ(200, response_code); // HTTP OK
EXPECT_FALSE(data.empty());
return CloudPrintURLFetcher::CONTINUE_PROCESSING;
}
CloudPrintURLFetcher::ResponseAction
CloudPrintURLFetcherBasicTest::HandleRawResponse(
const net::URLFetcher* source,
const GURL& url,
const net::URLRequestStatus& status,
int response_code,
const net::ResponseCookies& cookies,
const std::string& data) {
EXPECT_TRUE(status.is_success());
EXPECT_EQ(200, response_code); // HTTP OK
EXPECT_FALSE(data.empty());
if (handle_raw_response_) {
// If the current message loop is not the IO loop, it will be shut down when
// the main loop returns and this thread subsequently goes out of scope.
io_task_runner()->PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
return CloudPrintURLFetcher::STOP_PROCESSING;
}
return CloudPrintURLFetcher::CONTINUE_PROCESSING;
}
CloudPrintURLFetcher::ResponseAction
CloudPrintURLFetcherBasicTest::HandleRawData(
const net::URLFetcher* source,
const GURL& url,
const std::string& data) {
// We should never get here if we returned true in HandleRawResponse
EXPECT_FALSE(handle_raw_response_);
if (handle_raw_data_) {
io_task_runner()->PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
return CloudPrintURLFetcher::STOP_PROCESSING;
}
return CloudPrintURLFetcher::CONTINUE_PROCESSING;
}
CloudPrintURLFetcher::ResponseAction
CloudPrintURLFetcherBasicTest::HandleJSONData(
const net::URLFetcher* source,
const GURL& url,
base::DictionaryValue* json_data,
bool succeeded) {
// We should never get here if we returned true in one of the above methods.
EXPECT_FALSE(handle_raw_response_);
EXPECT_FALSE(handle_raw_data_);
io_task_runner()->PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
return CloudPrintURLFetcher::STOP_PROCESSING;
}
CloudPrintURLFetcher::ResponseAction
CloudPrintURLFetcherOverloadTest::HandleRawData(
const net::URLFetcher* source,
const GURL& url,
const std::string& data) {
const TimeDelta one_second = TimeDelta::FromMilliseconds(1000);
response_count_++;
if (response_count_ < 20) {
fetcher_->StartGetRequest(CloudPrintURLFetcher::REQUEST_MAX, url, this,
max_retries_, std::string());
} else {
// We have already sent 20 requests continuously. And we expect that
// it takes more than 1 second due to the overload protection settings.
EXPECT_TRUE(Time::Now() - start_time_ >= one_second);
io_task_runner()->PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
}
return CloudPrintURLFetcher::STOP_PROCESSING;
}
CloudPrintURLFetcher::ResponseAction
CloudPrintURLFetcherRetryBackoffTest::HandleRawData(
const net::URLFetcher* source,
const GURL& url,
const std::string& data) {
response_count_++;
// First attempt + 11 retries = 12 total responses.
EXPECT_LE(response_count_, 12);
return CloudPrintURLFetcher::RETRY_REQUEST;
}
void CloudPrintURLFetcherRetryBackoffTest::OnRequestGiveUp() {
// It takes more than 200 ms to finish all 11 requests.
EXPECT_TRUE(Time::Now() - start_time_ >= TimeDelta::FromMilliseconds(200));
io_task_runner()->PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
}
TEST_F(CloudPrintURLFetcherBasicTest, HandleRawResponse) {
net::SpawnedTestServer test_server(net::SpawnedTestServer::TYPE_HTTP,
net::SpawnedTestServer::kLocalhost,
base::FilePath(kDocRoot));
ASSERT_TRUE(test_server.Start());
SetHandleRawResponse(true);
CreateFetcher(test_server.GetURL("echo"), 0);
base::MessageLoop::current()->Run();
}
TEST_F(CloudPrintURLFetcherBasicTest, HandleRawData) {
net::SpawnedTestServer test_server(net::SpawnedTestServer::TYPE_HTTP,
net::SpawnedTestServer::kLocalhost,
base::FilePath(kDocRoot));
ASSERT_TRUE(test_server.Start());
SetHandleRawData(true);
CreateFetcher(test_server.GetURL("echo"), 0);
base::MessageLoop::current()->Run();
}
TEST_F(CloudPrintURLFetcherOverloadTest, Protect) {
net::SpawnedTestServer test_server(net::SpawnedTestServer::TYPE_HTTP,
net::SpawnedTestServer::kLocalhost,
base::FilePath(kDocRoot));
ASSERT_TRUE(test_server.Start());
GURL url(test_server.GetURL("defaultresponse"));
CreateFetcher(url, 11);
base::MessageLoop::current()->Run();
}
TEST_F(CloudPrintURLFetcherRetryBackoffTest, GiveUp) {
net::SpawnedTestServer test_server(net::SpawnedTestServer::TYPE_HTTP,
net::SpawnedTestServer::kLocalhost,
base::FilePath(kDocRoot));
ASSERT_TRUE(test_server.Start());
GURL url(test_server.GetURL("defaultresponse"));
CreateFetcher(url, 11);
base::MessageLoop::current()->Run();
}
} // namespace cloud_print