Home | History | Annotate | Download | only in cloud_print
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/command_line.h"
      6 #include "base/memory/ref_counted.h"
      7 #include "base/message_loop/message_loop_proxy.h"
      8 #include "base/synchronization/waitable_event.h"
      9 #include "base/threading/thread.h"
     10 #include "base/values.h"
     11 #include "chrome/service/cloud_print/cloud_print_url_fetcher.h"
     12 #include "chrome/service/service_process.h"
     13 #include "net/test/spawned_test_server/spawned_test_server.h"
     14 #include "net/url_request/url_request_context_getter.h"
     15 #include "net/url_request/url_request_status.h"
     16 #include "net/url_request/url_request_test_util.h"
     17 #include "net/url_request/url_request_throttler_manager.h"
     18 #include "testing/gtest/include/gtest/gtest.h"
     19 #include "url/gurl.h"
     20 
     21 using base::Time;
     22 using base::TimeDelta;
     23 
     24 namespace cloud_print {
     25 
     26 const base::FilePath::CharType kDocRoot[] =
     27     FILE_PATH_LITERAL("chrome/test/data");
     28 
     29 int g_request_context_getter_instances = 0;
     30 class TrackingTestURLRequestContextGetter
     31     : public net::TestURLRequestContextGetter {
     32  public:
     33   explicit TrackingTestURLRequestContextGetter(
     34       base::MessageLoopProxy* io_message_loop_proxy,
     35       net::URLRequestThrottlerManager* throttler_manager)
     36       : TestURLRequestContextGetter(io_message_loop_proxy),
     37         throttler_manager_(throttler_manager) {
     38     g_request_context_getter_instances++;
     39   }
     40 
     41   virtual net::TestURLRequestContext* GetURLRequestContext() OVERRIDE {
     42     if (!context_.get()) {
     43       context_.reset(new net::TestURLRequestContext(true));
     44       context_->set_throttler_manager(throttler_manager_);
     45       context_->Init();
     46     }
     47     return context_.get();
     48   }
     49 
     50  protected:
     51   virtual ~TrackingTestURLRequestContextGetter() {
     52     g_request_context_getter_instances--;
     53   }
     54 
     55  private:
     56   // Not owned here.
     57   net::URLRequestThrottlerManager* throttler_manager_;
     58   scoped_ptr<net::TestURLRequestContext> context_;
     59 };
     60 
     61 class TestCloudPrintURLFetcher : public CloudPrintURLFetcher {
     62  public:
     63   explicit TestCloudPrintURLFetcher(
     64       base::MessageLoopProxy* io_message_loop_proxy)
     65       : io_message_loop_proxy_(io_message_loop_proxy) {
     66   }
     67 
     68   virtual net::URLRequestContextGetter* GetRequestContextGetter() OVERRIDE {
     69     return new TrackingTestURLRequestContextGetter(
     70         io_message_loop_proxy_.get(), throttler_manager());
     71   }
     72 
     73   net::URLRequestThrottlerManager* throttler_manager() {
     74     return &throttler_manager_;
     75   }
     76 
     77  private:
     78   virtual ~TestCloudPrintURLFetcher() {}
     79 
     80   scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_;
     81 
     82   // We set this as the throttler manager for the
     83   // TestURLRequestContext we create.
     84   net::URLRequestThrottlerManager throttler_manager_;
     85 };
     86 
     87 class CloudPrintURLFetcherTest : public testing::Test,
     88                                  public CloudPrintURLFetcherDelegate {
     89  public:
     90   CloudPrintURLFetcherTest() : max_retries_(0), fetcher_(NULL) { }
     91 
     92   // Creates a URLFetcher, using the program's main thread to do IO.
     93   virtual void CreateFetcher(const GURL& url, int max_retries);
     94 
     95   // CloudPrintURLFetcher::Delegate
     96   virtual CloudPrintURLFetcher::ResponseAction HandleRawResponse(
     97       const net::URLFetcher* source,
     98       const GURL& url,
     99       const net::URLRequestStatus& status,
    100       int response_code,
    101       const net::ResponseCookies& cookies,
    102       const std::string& data) OVERRIDE;
    103 
    104   virtual CloudPrintURLFetcher::ResponseAction OnRequestAuthError() OVERRIDE {
    105     ADD_FAILURE();
    106     return CloudPrintURLFetcher::STOP_PROCESSING;
    107   }
    108 
    109   virtual std::string GetAuthHeader() OVERRIDE {
    110     return std::string();
    111   }
    112 
    113   scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy() {
    114     return io_message_loop_proxy_;
    115   }
    116 
    117  protected:
    118   virtual void SetUp() {
    119     testing::Test::SetUp();
    120 
    121     io_message_loop_proxy_ = base::MessageLoopProxy::current();
    122   }
    123 
    124   virtual void TearDown() {
    125     fetcher_ = NULL;
    126     // Deleting the fetcher causes a task to be posted to the IO thread to
    127     // release references to the URLRequestContextGetter. We need to run all
    128     // pending tasks to execute that (this is the IO thread).
    129     base::MessageLoop::current()->RunUntilIdle();
    130     EXPECT_EQ(0, g_request_context_getter_instances);
    131   }
    132 
    133   // URLFetcher is designed to run on the main UI thread, but in our tests
    134   // we assume that the current thread is the IO thread where the URLFetcher
    135   // dispatches its requests to.  When we wish to simulate being used from
    136   // a UI thread, we dispatch a worker thread to do so.
    137   base::MessageLoopForIO io_loop_;
    138   scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_;
    139   int max_retries_;
    140   Time start_time_;
    141   scoped_refptr<TestCloudPrintURLFetcher> fetcher_;
    142 };
    143 
    144 class CloudPrintURLFetcherBasicTest : public CloudPrintURLFetcherTest {
    145  public:
    146   CloudPrintURLFetcherBasicTest()
    147       : handle_raw_response_(false), handle_raw_data_(false) { }
    148   // CloudPrintURLFetcher::Delegate
    149   virtual CloudPrintURLFetcher::ResponseAction HandleRawResponse(
    150       const net::URLFetcher* source,
    151       const GURL& url,
    152       const net::URLRequestStatus& status,
    153       int response_code,
    154       const net::ResponseCookies& cookies,
    155       const std::string& data) OVERRIDE;
    156 
    157   virtual CloudPrintURLFetcher::ResponseAction HandleRawData(
    158       const net::URLFetcher* source,
    159       const GURL& url,
    160       const std::string& data) OVERRIDE;
    161 
    162   virtual CloudPrintURLFetcher::ResponseAction HandleJSONData(
    163       const net::URLFetcher* source,
    164       const GURL& url,
    165       base::DictionaryValue* json_data,
    166       bool succeeded) OVERRIDE;
    167 
    168   void SetHandleRawResponse(bool handle_raw_response) {
    169     handle_raw_response_ = handle_raw_response;
    170   }
    171   void SetHandleRawData(bool handle_raw_data) {
    172     handle_raw_data_ = handle_raw_data;
    173   }
    174  private:
    175   bool handle_raw_response_;
    176   bool handle_raw_data_;
    177 };
    178 
    179 // Version of CloudPrintURLFetcherTest that tests overload protection.
    180 class CloudPrintURLFetcherOverloadTest : public CloudPrintURLFetcherTest {
    181  public:
    182   CloudPrintURLFetcherOverloadTest() : response_count_(0) {
    183   }
    184 
    185   // CloudPrintURLFetcher::Delegate
    186   virtual CloudPrintURLFetcher::ResponseAction HandleRawData(
    187       const net::URLFetcher* source,
    188       const GURL& url,
    189       const std::string& data) OVERRIDE;
    190 
    191  private:
    192   int response_count_;
    193 };
    194 
    195 // Version of CloudPrintURLFetcherTest that tests backoff protection.
    196 class CloudPrintURLFetcherRetryBackoffTest : public CloudPrintURLFetcherTest {
    197  public:
    198   CloudPrintURLFetcherRetryBackoffTest() : response_count_(0) {
    199   }
    200 
    201   // CloudPrintURLFetcher::Delegate
    202   virtual CloudPrintURLFetcher::ResponseAction HandleRawData(
    203       const net::URLFetcher* source,
    204       const GURL& url,
    205       const std::string& data) OVERRIDE;
    206 
    207   virtual void OnRequestGiveUp() OVERRIDE;
    208 
    209  private:
    210   int response_count_;
    211 };
    212 
    213 
    214 void CloudPrintURLFetcherTest::CreateFetcher(const GURL& url, int max_retries) {
    215   fetcher_ = new TestCloudPrintURLFetcher(io_message_loop_proxy().get());
    216 
    217   // Registers an entry for test url. It only allows 3 requests to be sent
    218   // in 200 milliseconds.
    219   scoped_refptr<net::URLRequestThrottlerEntry>
    220   entry(new net::URLRequestThrottlerEntry(
    221       fetcher_->throttler_manager(), std::string(), 200, 3, 1, 2.0, 0.0, 256));
    222   fetcher_->throttler_manager()->OverrideEntryForTests(url, entry.get());
    223 
    224   max_retries_ = max_retries;
    225   start_time_ = Time::Now();
    226   fetcher_->StartGetRequest(CloudPrintURLFetcher::REQUEST_MAX, url, this,
    227                             max_retries_, std::string());
    228 }
    229 
    230 CloudPrintURLFetcher::ResponseAction
    231 CloudPrintURLFetcherTest::HandleRawResponse(
    232     const net::URLFetcher* source,
    233     const GURL& url,
    234     const net::URLRequestStatus& status,
    235     int response_code,
    236     const net::ResponseCookies& cookies,
    237     const std::string& data) {
    238   EXPECT_TRUE(status.is_success());
    239   EXPECT_EQ(200, response_code);  // HTTP OK
    240   EXPECT_FALSE(data.empty());
    241   return CloudPrintURLFetcher::CONTINUE_PROCESSING;
    242 }
    243 
    244 CloudPrintURLFetcher::ResponseAction
    245 CloudPrintURLFetcherBasicTest::HandleRawResponse(
    246     const net::URLFetcher* source,
    247     const GURL& url,
    248     const net::URLRequestStatus& status,
    249     int response_code,
    250     const net::ResponseCookies& cookies,
    251     const std::string& data) {
    252   EXPECT_TRUE(status.is_success());
    253   EXPECT_EQ(200, response_code);  // HTTP OK
    254   EXPECT_FALSE(data.empty());
    255 
    256   if (handle_raw_response_) {
    257     // If the current message loop is not the IO loop, it will be shut down when
    258     // the main loop returns and this thread subsequently goes out of scope.
    259     io_message_loop_proxy()->PostTask(FROM_HERE,
    260                                       base::MessageLoop::QuitClosure());
    261     return CloudPrintURLFetcher::STOP_PROCESSING;
    262   }
    263   return CloudPrintURLFetcher::CONTINUE_PROCESSING;
    264 }
    265 
    266 CloudPrintURLFetcher::ResponseAction
    267 CloudPrintURLFetcherBasicTest::HandleRawData(
    268     const net::URLFetcher* source,
    269     const GURL& url,
    270     const std::string& data) {
    271   // We should never get here if we returned true in HandleRawResponse
    272   EXPECT_FALSE(handle_raw_response_);
    273   if (handle_raw_data_) {
    274     io_message_loop_proxy()->PostTask(FROM_HERE,
    275                                       base::MessageLoop::QuitClosure());
    276     return CloudPrintURLFetcher::STOP_PROCESSING;
    277   }
    278   return CloudPrintURLFetcher::CONTINUE_PROCESSING;
    279 }
    280 
    281 CloudPrintURLFetcher::ResponseAction
    282 CloudPrintURLFetcherBasicTest::HandleJSONData(
    283     const net::URLFetcher* source,
    284     const GURL& url,
    285     base::DictionaryValue* json_data,
    286     bool succeeded) {
    287   // We should never get here if we returned true in one of the above methods.
    288   EXPECT_FALSE(handle_raw_response_);
    289   EXPECT_FALSE(handle_raw_data_);
    290   io_message_loop_proxy()->PostTask(FROM_HERE,
    291                                     base::MessageLoop::QuitClosure());
    292   return CloudPrintURLFetcher::STOP_PROCESSING;
    293 }
    294 
    295 CloudPrintURLFetcher::ResponseAction
    296 CloudPrintURLFetcherOverloadTest::HandleRawData(
    297     const net::URLFetcher* source,
    298     const GURL& url,
    299     const std::string& data) {
    300   const TimeDelta one_second = TimeDelta::FromMilliseconds(1000);
    301   response_count_++;
    302   if (response_count_ < 20) {
    303     fetcher_->StartGetRequest(CloudPrintURLFetcher::REQUEST_MAX, url, this,
    304                               max_retries_, std::string());
    305   } else {
    306     // We have already sent 20 requests continuously. And we expect that
    307     // it takes more than 1 second due to the overload protection settings.
    308     EXPECT_TRUE(Time::Now() - start_time_ >= one_second);
    309     io_message_loop_proxy()->PostTask(FROM_HERE,
    310                                       base::MessageLoop::QuitClosure());
    311   }
    312   return CloudPrintURLFetcher::STOP_PROCESSING;
    313 }
    314 
    315 CloudPrintURLFetcher::ResponseAction
    316 CloudPrintURLFetcherRetryBackoffTest::HandleRawData(
    317     const net::URLFetcher* source,
    318     const GURL& url,
    319     const std::string& data) {
    320   response_count_++;
    321   // First attempt + 11 retries = 12 total responses.
    322   EXPECT_LE(response_count_, 12);
    323   return CloudPrintURLFetcher::RETRY_REQUEST;
    324 }
    325 
    326 void CloudPrintURLFetcherRetryBackoffTest::OnRequestGiveUp() {
    327   // It takes more than 200 ms to finish all 11 requests.
    328   EXPECT_TRUE(Time::Now() - start_time_ >= TimeDelta::FromMilliseconds(200));
    329   io_message_loop_proxy()->PostTask(FROM_HERE,
    330                                     base::MessageLoop::QuitClosure());
    331 }
    332 
    333 TEST_F(CloudPrintURLFetcherBasicTest, HandleRawResponse) {
    334   net::SpawnedTestServer test_server(net::SpawnedTestServer::TYPE_HTTP,
    335                                      net::SpawnedTestServer::kLocalhost,
    336                                      base::FilePath(kDocRoot));
    337   ASSERT_TRUE(test_server.Start());
    338   SetHandleRawResponse(true);
    339 
    340   CreateFetcher(test_server.GetURL("echo"), 0);
    341   base::MessageLoop::current()->Run();
    342 }
    343 
    344 TEST_F(CloudPrintURLFetcherBasicTest, HandleRawData) {
    345   net::SpawnedTestServer test_server(net::SpawnedTestServer::TYPE_HTTP,
    346                                      net::SpawnedTestServer::kLocalhost,
    347                                      base::FilePath(kDocRoot));
    348   ASSERT_TRUE(test_server.Start());
    349 
    350   SetHandleRawData(true);
    351   CreateFetcher(test_server.GetURL("echo"), 0);
    352   base::MessageLoop::current()->Run();
    353 }
    354 
    355 TEST_F(CloudPrintURLFetcherOverloadTest, Protect) {
    356   net::SpawnedTestServer test_server(net::SpawnedTestServer::TYPE_HTTP,
    357                                      net::SpawnedTestServer::kLocalhost,
    358                                      base::FilePath(kDocRoot));
    359   ASSERT_TRUE(test_server.Start());
    360 
    361   GURL url(test_server.GetURL("defaultresponse"));
    362   CreateFetcher(url, 11);
    363 
    364   base::MessageLoop::current()->Run();
    365 }
    366 
    367 TEST_F(CloudPrintURLFetcherRetryBackoffTest, GiveUp) {
    368   net::SpawnedTestServer test_server(net::SpawnedTestServer::TYPE_HTTP,
    369                                      net::SpawnedTestServer::kLocalhost,
    370                                      base::FilePath(kDocRoot));
    371   ASSERT_TRUE(test_server.Start());
    372 
    373   GURL url(test_server.GetURL("defaultresponse"));
    374   CreateFetcher(url, 11);
    375 
    376   base::MessageLoop::current()->Run();
    377 }
    378 
    379 }  // namespace cloud_print
    380