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.
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"
22 using base::TimeDelta
;
24 namespace cloud_print
{
26 const base::FilePath::CharType kDocRoot
[] =
27 FILE_PATH_LITERAL("chrome/test/data");
29 int g_request_context_getter_instances
= 0;
30 class TrackingTestURLRequestContextGetter
31 : public net::TestURLRequestContextGetter
{
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
++;
41 virtual net::TestURLRequestContext
* GetURLRequestContext() OVERRIDE
{
42 if (!context_
.get()) {
43 context_
.reset(new net::TestURLRequestContext(true));
44 context_
->set_throttler_manager(throttler_manager_
);
47 return context_
.get();
51 virtual ~TrackingTestURLRequestContextGetter() {
52 g_request_context_getter_instances
--;
57 net::URLRequestThrottlerManager
* throttler_manager_
;
58 scoped_ptr
<net::TestURLRequestContext
> context_
;
61 class TestCloudPrintURLFetcher
: public CloudPrintURLFetcher
{
63 explicit TestCloudPrintURLFetcher(
64 base::MessageLoopProxy
* io_message_loop_proxy
)
65 : io_message_loop_proxy_(io_message_loop_proxy
) {
68 virtual net::URLRequestContextGetter
* GetRequestContextGetter() OVERRIDE
{
69 return new TrackingTestURLRequestContextGetter(
70 io_message_loop_proxy_
.get(), throttler_manager());
73 net::URLRequestThrottlerManager
* throttler_manager() {
74 return &throttler_manager_
;
78 virtual ~TestCloudPrintURLFetcher() {}
80 scoped_refptr
<base::MessageLoopProxy
> io_message_loop_proxy_
;
82 // We set this as the throttler manager for the
83 // TestURLRequestContext we create.
84 net::URLRequestThrottlerManager throttler_manager_
;
87 class CloudPrintURLFetcherTest
: public testing::Test
,
88 public CloudPrintURLFetcherDelegate
{
90 CloudPrintURLFetcherTest() : max_retries_(0), fetcher_(NULL
) { }
92 // Creates a URLFetcher, using the program's main thread to do IO.
93 virtual void CreateFetcher(const GURL
& url
, int max_retries
);
95 // CloudPrintURLFetcher::Delegate
96 virtual CloudPrintURLFetcher::ResponseAction
HandleRawResponse(
97 const net::URLFetcher
* source
,
99 const net::URLRequestStatus
& status
,
101 const net::ResponseCookies
& cookies
,
102 const std::string
& data
) OVERRIDE
;
104 virtual CloudPrintURLFetcher::ResponseAction
OnRequestAuthError() OVERRIDE
{
106 return CloudPrintURLFetcher::STOP_PROCESSING
;
109 virtual std::string
GetAuthHeader() OVERRIDE
{
110 return std::string();
113 scoped_refptr
<base::MessageLoopProxy
> io_message_loop_proxy() {
114 return io_message_loop_proxy_
;
118 virtual void SetUp() {
119 testing::Test::SetUp();
121 io_message_loop_proxy_
= base::MessageLoopProxy::current();
124 virtual void TearDown() {
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
);
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_
;
141 scoped_refptr
<TestCloudPrintURLFetcher
> fetcher_
;
144 class CloudPrintURLFetcherBasicTest
: public CloudPrintURLFetcherTest
{
146 CloudPrintURLFetcherBasicTest()
147 : handle_raw_response_(false), handle_raw_data_(false) { }
148 // CloudPrintURLFetcher::Delegate
149 virtual CloudPrintURLFetcher::ResponseAction
HandleRawResponse(
150 const net::URLFetcher
* source
,
152 const net::URLRequestStatus
& status
,
154 const net::ResponseCookies
& cookies
,
155 const std::string
& data
) OVERRIDE
;
157 virtual CloudPrintURLFetcher::ResponseAction
HandleRawData(
158 const net::URLFetcher
* source
,
160 const std::string
& data
) OVERRIDE
;
162 virtual CloudPrintURLFetcher::ResponseAction
HandleJSONData(
163 const net::URLFetcher
* source
,
165 base::DictionaryValue
* json_data
,
166 bool succeeded
) OVERRIDE
;
168 void SetHandleRawResponse(bool handle_raw_response
) {
169 handle_raw_response_
= handle_raw_response
;
171 void SetHandleRawData(bool handle_raw_data
) {
172 handle_raw_data_
= handle_raw_data
;
175 bool handle_raw_response_
;
176 bool handle_raw_data_
;
179 // Version of CloudPrintURLFetcherTest that tests overload protection.
180 class CloudPrintURLFetcherOverloadTest
: public CloudPrintURLFetcherTest
{
182 CloudPrintURLFetcherOverloadTest() : response_count_(0) {
185 // CloudPrintURLFetcher::Delegate
186 virtual CloudPrintURLFetcher::ResponseAction
HandleRawData(
187 const net::URLFetcher
* source
,
189 const std::string
& data
) OVERRIDE
;
195 // Version of CloudPrintURLFetcherTest that tests backoff protection.
196 class CloudPrintURLFetcherRetryBackoffTest
: public CloudPrintURLFetcherTest
{
198 CloudPrintURLFetcherRetryBackoffTest() : response_count_(0) {
201 // CloudPrintURLFetcher::Delegate
202 virtual CloudPrintURLFetcher::ResponseAction
HandleRawData(
203 const net::URLFetcher
* source
,
205 const std::string
& data
) OVERRIDE
;
207 virtual void OnRequestGiveUp() OVERRIDE
;
214 void CloudPrintURLFetcherTest::CreateFetcher(const GURL
& url
, int max_retries
) {
215 fetcher_
= new TestCloudPrintURLFetcher(io_message_loop_proxy().get());
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());
224 max_retries_
= max_retries
;
225 start_time_
= Time::Now();
226 fetcher_
->StartGetRequest(CloudPrintURLFetcher::REQUEST_MAX
, url
, this,
227 max_retries_
, std::string());
230 CloudPrintURLFetcher::ResponseAction
231 CloudPrintURLFetcherTest::HandleRawResponse(
232 const net::URLFetcher
* source
,
234 const net::URLRequestStatus
& status
,
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
;
244 CloudPrintURLFetcher::ResponseAction
245 CloudPrintURLFetcherBasicTest::HandleRawResponse(
246 const net::URLFetcher
* source
,
248 const net::URLRequestStatus
& status
,
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());
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
;
263 return CloudPrintURLFetcher::CONTINUE_PROCESSING
;
266 CloudPrintURLFetcher::ResponseAction
267 CloudPrintURLFetcherBasicTest::HandleRawData(
268 const net::URLFetcher
* source
,
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
;
278 return CloudPrintURLFetcher::CONTINUE_PROCESSING
;
281 CloudPrintURLFetcher::ResponseAction
282 CloudPrintURLFetcherBasicTest::HandleJSONData(
283 const net::URLFetcher
* source
,
285 base::DictionaryValue
* json_data
,
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
;
295 CloudPrintURLFetcher::ResponseAction
296 CloudPrintURLFetcherOverloadTest::HandleRawData(
297 const net::URLFetcher
* source
,
299 const std::string
& data
) {
300 const TimeDelta one_second
= TimeDelta::FromMilliseconds(1000);
302 if (response_count_
< 20) {
303 fetcher_
->StartGetRequest(CloudPrintURLFetcher::REQUEST_MAX
, url
, this,
304 max_retries_
, std::string());
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());
312 return CloudPrintURLFetcher::STOP_PROCESSING
;
315 CloudPrintURLFetcher::ResponseAction
316 CloudPrintURLFetcherRetryBackoffTest::HandleRawData(
317 const net::URLFetcher
* source
,
319 const std::string
& data
) {
321 // First attempt + 11 retries = 12 total responses.
322 EXPECT_LE(response_count_
, 12);
323 return CloudPrintURLFetcher::RETRY_REQUEST
;
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());
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);
340 CreateFetcher(test_server
.GetURL("echo"), 0);
341 base::MessageLoop::current()->Run();
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());
350 SetHandleRawData(true);
351 CreateFetcher(test_server
.GetURL("echo"), 0);
352 base::MessageLoop::current()->Run();
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());
361 GURL
url(test_server
.GetURL("defaultresponse"));
362 CreateFetcher(url
, 11);
364 base::MessageLoop::current()->Run();
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());
373 GURL
url(test_server
.GetURL("defaultresponse"));
374 CreateFetcher(url
, 11);
376 base::MessageLoop::current()->Run();
379 } // namespace cloud_print