| // Copyright (c) 2011 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/memory/ref_counted.h" |
| #include "base/message_loop_proxy.h" |
| #include "base/synchronization/waitable_event.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 "googleurl/src/gurl.h" |
| #include "net/test/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" |
| |
| using base::Time; |
| using base::TimeDelta; |
| |
| namespace { |
| |
| const FilePath::CharType kDocRoot[] = FILE_PATH_LITERAL("chrome/test/data"); |
| |
| int g_request_context_getter_instances = 0; |
| class TestURLRequestContextGetter : public net::URLRequestContextGetter { |
| public: |
| explicit TestURLRequestContextGetter( |
| base::MessageLoopProxy* io_message_loop_proxy) |
| : io_message_loop_proxy_(io_message_loop_proxy) { |
| g_request_context_getter_instances++; |
| } |
| virtual net::URLRequestContext* GetURLRequestContext() { |
| if (!context_) |
| context_ = new TestURLRequestContext(); |
| return context_; |
| } |
| virtual scoped_refptr<base::MessageLoopProxy> GetIOMessageLoopProxy() const { |
| return io_message_loop_proxy_; |
| } |
| |
| protected: |
| scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_; |
| |
| private: |
| virtual ~TestURLRequestContextGetter() { |
| g_request_context_getter_instances--; |
| } |
| |
| scoped_refptr<net::URLRequestContext> context_; |
| }; |
| |
| class TestCloudPrintURLFetcher : public CloudPrintURLFetcher { |
| public: |
| explicit TestCloudPrintURLFetcher( |
| base::MessageLoopProxy* io_message_loop_proxy) |
| : io_message_loop_proxy_(io_message_loop_proxy) { |
| } |
| |
| virtual net::URLRequestContextGetter* GetRequestContextGetter() { |
| return new TestURLRequestContextGetter(io_message_loop_proxy_.get()); |
| } |
| private: |
| scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_; |
| }; |
| |
| 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 |
| virtual CloudPrintURLFetcher::ResponseAction HandleRawResponse( |
| const content::URLFetcher* source, |
| const GURL& url, |
| const net::URLRequestStatus& status, |
| int response_code, |
| const net::ResponseCookies& cookies, |
| const std::string& data); |
| |
| virtual void OnRequestAuthError() { |
| ADD_FAILURE(); |
| } |
| |
| scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy() { |
| return io_message_loop_proxy_; |
| } |
| |
| protected: |
| virtual void SetUp() { |
| testing::Test::SetUp(); |
| |
| io_message_loop_proxy_ = base::MessageLoopProxy::current(); |
| } |
| |
| virtual void TearDown() { |
| 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). |
| MessageLoop::current()->RunAllPending(); |
| 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. |
| MessageLoopForIO io_loop_; |
| scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_; |
| int max_retries_; |
| Time start_time_; |
| scoped_refptr<CloudPrintURLFetcher> fetcher_; |
| }; |
| |
| class CloudPrintURLFetcherBasicTest : public CloudPrintURLFetcherTest { |
| public: |
| CloudPrintURLFetcherBasicTest() |
| : handle_raw_response_(false), handle_raw_data_(false) { } |
| // CloudPrintURLFetcher::Delegate |
| virtual CloudPrintURLFetcher::ResponseAction HandleRawResponse( |
| const content::URLFetcher* source, |
| const GURL& url, |
| const net::URLRequestStatus& status, |
| int response_code, |
| const net::ResponseCookies& cookies, |
| const std::string& data); |
| |
| virtual CloudPrintURLFetcher::ResponseAction HandleRawData( |
| const content::URLFetcher* source, |
| const GURL& url, |
| const std::string& data); |
| |
| virtual CloudPrintURLFetcher::ResponseAction HandleJSONData( |
| const content::URLFetcher* source, |
| const GURL& url, |
| DictionaryValue* json_data, |
| bool succeeded); |
| |
| 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 |
| virtual CloudPrintURLFetcher::ResponseAction HandleRawData( |
| const content::URLFetcher* source, |
| const GURL& url, |
| const std::string& data); |
| |
| private: |
| int response_count_; |
| }; |
| |
| // Version of CloudPrintURLFetcherTest that tests backoff protection. |
| class CloudPrintURLFetcherRetryBackoffTest : public CloudPrintURLFetcherTest { |
| public: |
| CloudPrintURLFetcherRetryBackoffTest() : response_count_(0) { |
| } |
| |
| // CloudPrintURLFetcher::Delegate |
| virtual CloudPrintURLFetcher::ResponseAction HandleRawData( |
| const content::URLFetcher* source, |
| const GURL& url, |
| const std::string& data); |
| |
| virtual void OnRequestGiveUp(); |
| |
| private: |
| int response_count_; |
| }; |
| |
| |
| void CloudPrintURLFetcherTest::CreateFetcher(const GURL& url, int max_retries) { |
| fetcher_ = new TestCloudPrintURLFetcher(io_message_loop_proxy()); |
| max_retries_ = max_retries; |
| start_time_ = Time::Now(); |
| fetcher_->StartGetRequest(url, this, max_retries_, std::string()); |
| } |
| |
| CloudPrintURLFetcher::ResponseAction |
| CloudPrintURLFetcherTest::HandleRawResponse( |
| const content::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 content::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_message_loop_proxy()->PostTask(FROM_HERE, new MessageLoop::QuitTask()); |
| return CloudPrintURLFetcher::STOP_PROCESSING; |
| } |
| return CloudPrintURLFetcher::CONTINUE_PROCESSING; |
| } |
| |
| CloudPrintURLFetcher::ResponseAction |
| CloudPrintURLFetcherBasicTest::HandleRawData( |
| const content::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_message_loop_proxy()->PostTask(FROM_HERE, new MessageLoop::QuitTask()); |
| return CloudPrintURLFetcher::STOP_PROCESSING; |
| } |
| return CloudPrintURLFetcher::CONTINUE_PROCESSING; |
| } |
| |
| CloudPrintURLFetcher::ResponseAction |
| CloudPrintURLFetcherBasicTest::HandleJSONData( |
| const content::URLFetcher* source, |
| const GURL& url, |
| 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_message_loop_proxy()->PostTask(FROM_HERE, new MessageLoop::QuitTask()); |
| return CloudPrintURLFetcher::STOP_PROCESSING; |
| } |
| |
| CloudPrintURLFetcher::ResponseAction |
| CloudPrintURLFetcherOverloadTest::HandleRawData( |
| const content::URLFetcher* source, |
| const GURL& url, |
| const std::string& data) { |
| const TimeDelta one_second = TimeDelta::FromMilliseconds(1000); |
| response_count_++; |
| if (response_count_ < 20) { |
| fetcher_->StartGetRequest(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_message_loop_proxy()->PostTask(FROM_HERE, new MessageLoop::QuitTask()); |
| } |
| return CloudPrintURLFetcher::STOP_PROCESSING; |
| } |
| |
| CloudPrintURLFetcher::ResponseAction |
| CloudPrintURLFetcherRetryBackoffTest::HandleRawData( |
| const content::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_message_loop_proxy()->PostTask(FROM_HERE, new MessageLoop::QuitTask()); |
| } |
| |
| // http://code.google.com/p/chromium/issues/detail?id=60426 |
| TEST_F(CloudPrintURLFetcherBasicTest, FLAKY_HandleRawResponse) { |
| net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot)); |
| ASSERT_TRUE(test_server.Start()); |
| SetHandleRawResponse(true); |
| |
| CreateFetcher(test_server.GetURL("echo"), 0); |
| MessageLoop::current()->Run(); |
| } |
| |
| // http://code.google.com/p/chromium/issues/detail?id=60426 |
| TEST_F(CloudPrintURLFetcherBasicTest, FLAKY_HandleRawData) { |
| net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot)); |
| ASSERT_TRUE(test_server.Start()); |
| |
| SetHandleRawData(true); |
| CreateFetcher(test_server.GetURL("echo"), 0); |
| MessageLoop::current()->Run(); |
| } |
| |
| TEST_F(CloudPrintURLFetcherOverloadTest, Protect) { |
| net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot)); |
| ASSERT_TRUE(test_server.Start()); |
| |
| GURL url(test_server.GetURL("defaultresponse")); |
| |
| // Registers an entry for test url. It only allows 3 requests to be sent |
| // in 200 milliseconds. |
| net::URLRequestThrottlerManager* manager = |
| net::URLRequestThrottlerManager::GetInstance(); |
| scoped_refptr<net::URLRequestThrottlerEntry> entry( |
| new net::URLRequestThrottlerEntry(manager, "", 200, 3, 1, 2.0, 0.0, 256)); |
| manager->OverrideEntryForTests(url, entry); |
| |
| CreateFetcher(url, 11); |
| |
| MessageLoop::current()->Run(); |
| |
| net::URLRequestThrottlerManager::GetInstance()->EraseEntryForTests(url); |
| } |
| |
| // http://code.google.com/p/chromium/issues/detail?id=60426 |
| TEST_F(CloudPrintURLFetcherRetryBackoffTest, FLAKY_GiveUp) { |
| net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot)); |
| ASSERT_TRUE(test_server.Start()); |
| |
| GURL url(test_server.GetURL("defaultresponse")); |
| |
| // Registers an entry for test url. The backoff time is calculated by: |
| // new_backoff = 2.0 * old_backoff + 0 |
| // and maximum backoff time is 256 milliseconds. |
| // Maximum retries allowed is set to 11. |
| net::URLRequestThrottlerManager* manager = |
| net::URLRequestThrottlerManager::GetInstance(); |
| scoped_refptr<net::URLRequestThrottlerEntry> entry( |
| new net::URLRequestThrottlerEntry(manager, "", 200, 3, 1, 2.0, 0.0, 256)); |
| manager->OverrideEntryForTests(url, entry); |
| |
| CreateFetcher(url, 11); |
| |
| MessageLoop::current()->Run(); |
| |
| net::URLRequestThrottlerManager::GetInstance()->EraseEntryForTests(url); |
| } |
| |
| } // namespace. |