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