url_fetcher_unittest.cc revision 4a5e2dc747d50c653511c68ccb2cfbfb740bd5a7
1// Copyright (c) 2010 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/message_loop_proxy.h" 6#include "base/thread.h" 7#include "base/waitable_event.h" 8#include "build/build_config.h" 9#include "chrome/common/chrome_plugin_lib.h" 10#include "chrome/common/net/url_fetcher.h" 11#include "chrome/common/net/url_fetcher_protect.h" 12#include "chrome/common/net/url_request_context_getter.h" 13#include "net/http/http_response_headers.h" 14#include "net/url_request/url_request_unittest.h" 15#include "net/test/test_server.h" 16#include "testing/gtest/include/gtest/gtest.h" 17 18#if defined(USE_NSS) 19#include "net/ocsp/nss_ocsp.h" 20#endif 21 22using base::Time; 23using base::TimeDelta; 24 25// TODO(eroman): Add a regression test for http://crbug.com/40505. 26 27namespace { 28 29const FilePath::CharType kDocRoot[] = FILE_PATH_LITERAL("chrome/test/data"); 30 31class TestURLRequestContextGetter : public URLRequestContextGetter { 32 public: 33 explicit TestURLRequestContextGetter( 34 base::MessageLoopProxy* io_message_loop_proxy) 35 : io_message_loop_proxy_(io_message_loop_proxy) { 36 } 37 virtual URLRequestContext* GetURLRequestContext() { 38 if (!context_) 39 context_ = new TestURLRequestContext(); 40 return context_; 41 } 42 virtual scoped_refptr<base::MessageLoopProxy> GetIOMessageLoopProxy() const { 43 return io_message_loop_proxy_; 44 } 45 46 protected: 47 scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_; 48 49 private: 50 ~TestURLRequestContextGetter() {} 51 52 scoped_refptr<URLRequestContext> context_; 53}; 54 55class URLFetcherTest : public testing::Test, public URLFetcher::Delegate { 56 public: 57 URLFetcherTest() : fetcher_(NULL) { } 58 59 // Creates a URLFetcher, using the program's main thread to do IO. 60 virtual void CreateFetcher(const GURL& url); 61 62 // URLFetcher::Delegate 63 virtual void OnURLFetchComplete(const URLFetcher* source, 64 const GURL& url, 65 const URLRequestStatus& status, 66 int response_code, 67 const ResponseCookies& cookies, 68 const std::string& data); 69 70 scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy() { 71 return io_message_loop_proxy_; 72 } 73 74 protected: 75 virtual void SetUp() { 76 testing::Test::SetUp(); 77 78 io_message_loop_proxy_ = base::MessageLoopProxy::CreateForCurrentThread(); 79 80 // Ensure that any plugin operations done by other tests are cleaned up. 81 ChromePluginLib::UnloadAllPlugins(); 82#if defined(USE_NSS) 83 net::EnsureOCSPInit(); 84#endif 85 } 86 87 virtual void TearDown() { 88#if defined(USE_NSS) 89 net::ShutdownOCSP(); 90#endif 91 } 92 93 // URLFetcher is designed to run on the main UI thread, but in our tests 94 // we assume that the current thread is the IO thread where the URLFetcher 95 // dispatches its requests to. When we wish to simulate being used from 96 // a UI thread, we dispatch a worker thread to do so. 97 MessageLoopForIO io_loop_; 98 scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_; 99 100 URLFetcher* fetcher_; 101}; 102 103// Version of URLFetcherTest that does a POST instead 104class URLFetcherPostTest : public URLFetcherTest { 105 public: 106 virtual void CreateFetcher(const GURL& url); 107 108 // URLFetcher::Delegate 109 virtual void OnURLFetchComplete(const URLFetcher* source, 110 const GURL& url, 111 const URLRequestStatus& status, 112 int response_code, 113 const ResponseCookies& cookies, 114 const std::string& data); 115}; 116 117// Version of URLFetcherTest that tests headers. 118class URLFetcherHeadersTest : public URLFetcherTest { 119 public: 120 // URLFetcher::Delegate 121 virtual void OnURLFetchComplete(const URLFetcher* source, 122 const GURL& url, 123 const URLRequestStatus& status, 124 int response_code, 125 const ResponseCookies& cookies, 126 const std::string& data); 127}; 128 129// Version of URLFetcherTest that tests overload protection. 130class URLFetcherProtectTest : public URLFetcherTest { 131 public: 132 virtual void CreateFetcher(const GURL& url); 133 // URLFetcher::Delegate 134 virtual void OnURLFetchComplete(const URLFetcher* source, 135 const GURL& url, 136 const URLRequestStatus& status, 137 int response_code, 138 const ResponseCookies& cookies, 139 const std::string& data); 140 private: 141 Time start_time_; 142}; 143 144// Version of URLFetcherTest that tests overload protection, when responses 145// passed through. 146class URLFetcherProtectTestPassedThrough : public URLFetcherTest { 147 public: 148 virtual void CreateFetcher(const GURL& url); 149 // URLFetcher::Delegate 150 virtual void OnURLFetchComplete(const URLFetcher* source, 151 const GURL& url, 152 const URLRequestStatus& status, 153 int response_code, 154 const ResponseCookies& cookies, 155 const std::string& data); 156 private: 157 Time start_time_; 158}; 159 160// Version of URLFetcherTest that tests bad HTTPS requests. 161class URLFetcherBadHTTPSTest : public URLFetcherTest { 162 public: 163 URLFetcherBadHTTPSTest(); 164 165 // URLFetcher::Delegate 166 virtual void OnURLFetchComplete(const URLFetcher* source, 167 const GURL& url, 168 const URLRequestStatus& status, 169 int response_code, 170 const ResponseCookies& cookies, 171 const std::string& data); 172 173 private: 174 FilePath cert_dir_; 175}; 176 177// Version of URLFetcherTest that tests request cancellation on shutdown. 178class URLFetcherCancelTest : public URLFetcherTest { 179 public: 180 virtual void CreateFetcher(const GURL& url); 181 // URLFetcher::Delegate 182 virtual void OnURLFetchComplete(const URLFetcher* source, 183 const GURL& url, 184 const URLRequestStatus& status, 185 int response_code, 186 const ResponseCookies& cookies, 187 const std::string& data); 188 189 void CancelRequest(); 190}; 191 192// Version of TestURLRequestContext that posts a Quit task to the IO 193// thread once it is deleted. 194class CancelTestURLRequestContext : public TestURLRequestContext { 195 virtual ~CancelTestURLRequestContext() { 196 // The d'tor should execute in the IO thread. Post the quit task to the 197 // current thread. 198 MessageLoop::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask()); 199 } 200}; 201 202class CancelTestURLRequestContextGetter : public URLRequestContextGetter { 203 public: 204 explicit CancelTestURLRequestContextGetter( 205 base::MessageLoopProxy* io_message_loop_proxy) 206 : io_message_loop_proxy_(io_message_loop_proxy), 207 context_created_(false, false) { 208 } 209 virtual URLRequestContext* GetURLRequestContext() { 210 if (!context_) { 211 context_ = new CancelTestURLRequestContext(); 212 context_created_.Signal(); 213 } 214 return context_; 215 } 216 virtual scoped_refptr<base::MessageLoopProxy> GetIOMessageLoopProxy() const { 217 return io_message_loop_proxy_; 218 } 219 void WaitForContextCreation() { 220 context_created_.Wait(); 221 } 222 223 private: 224 ~CancelTestURLRequestContextGetter() {} 225 226 scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_; 227 base::WaitableEvent context_created_; 228 scoped_refptr<URLRequestContext> context_; 229}; 230 231// Wrapper that lets us call CreateFetcher() on a thread of our choice. We 232// could make URLFetcherTest refcounted and use PostTask(FROM_HERE.. ) to call 233// CreateFetcher() directly, but the ownership of the URLFetcherTest is a bit 234// confusing in that case because GTest doesn't know about the refcounting. 235// It's less confusing to just do it this way. 236class FetcherWrapperTask : public Task { 237 public: 238 FetcherWrapperTask(URLFetcherTest* test, const GURL& url) 239 : test_(test), url_(url) { } 240 virtual void Run() { 241 test_->CreateFetcher(url_); 242 } 243 244 private: 245 URLFetcherTest* test_; 246 GURL url_; 247}; 248 249void URLFetcherTest::CreateFetcher(const GURL& url) { 250 fetcher_ = new URLFetcher(url, URLFetcher::GET, this); 251 fetcher_->set_request_context(new TestURLRequestContextGetter( 252 io_message_loop_proxy())); 253 fetcher_->Start(); 254} 255 256void URLFetcherTest::OnURLFetchComplete(const URLFetcher* source, 257 const GURL& url, 258 const URLRequestStatus& status, 259 int response_code, 260 const ResponseCookies& cookies, 261 const std::string& data) { 262 EXPECT_TRUE(status.is_success()); 263 EXPECT_EQ(200, response_code); // HTTP OK 264 EXPECT_FALSE(data.empty()); 265 266 delete fetcher_; // Have to delete this here and not in the destructor, 267 // because the destructor won't necessarily run on the 268 // same thread that CreateFetcher() did. 269 270 io_message_loop_proxy()->PostTask(FROM_HERE, new MessageLoop::QuitTask()); 271 // If the current message loop is not the IO loop, it will be shut down when 272 // the main loop returns and this thread subsequently goes out of scope. 273} 274 275void URLFetcherPostTest::CreateFetcher(const GURL& url) { 276 fetcher_ = new URLFetcher(url, URLFetcher::POST, this); 277 fetcher_->set_request_context(new TestURLRequestContextGetter( 278 io_message_loop_proxy())); 279 fetcher_->set_upload_data("application/x-www-form-urlencoded", 280 "bobsyeruncle"); 281 fetcher_->Start(); 282} 283 284void URLFetcherPostTest::OnURLFetchComplete(const URLFetcher* source, 285 const GURL& url, 286 const URLRequestStatus& status, 287 int response_code, 288 const ResponseCookies& cookies, 289 const std::string& data) { 290 EXPECT_EQ(std::string("bobsyeruncle"), data); 291 URLFetcherTest::OnURLFetchComplete(source, url, status, response_code, 292 cookies, data); 293} 294 295void URLFetcherHeadersTest::OnURLFetchComplete( 296 const URLFetcher* source, 297 const GURL& url, 298 const URLRequestStatus& status, 299 int response_code, 300 const ResponseCookies& cookies, 301 const std::string& data) { 302 std::string header; 303 EXPECT_TRUE(source->response_headers()->GetNormalizedHeader("cache-control", 304 &header)); 305 EXPECT_EQ("private", header); 306 URLFetcherTest::OnURLFetchComplete(source, url, status, response_code, 307 cookies, data); 308} 309 310void URLFetcherProtectTest::CreateFetcher(const GURL& url) { 311 fetcher_ = new URLFetcher(url, URLFetcher::GET, this); 312 fetcher_->set_request_context(new TestURLRequestContextGetter( 313 io_message_loop_proxy())); 314 start_time_ = Time::Now(); 315 fetcher_->Start(); 316} 317 318void URLFetcherProtectTest::OnURLFetchComplete(const URLFetcher* source, 319 const GURL& url, 320 const URLRequestStatus& status, 321 int response_code, 322 const ResponseCookies& cookies, 323 const std::string& data) { 324 const TimeDelta one_second = TimeDelta::FromMilliseconds(1000); 325 if (response_code >= 500) { 326 // Now running ServerUnavailable test. 327 // It takes more than 1 second to finish all 11 requests. 328 EXPECT_TRUE(Time::Now() - start_time_ >= one_second); 329 EXPECT_TRUE(status.is_success()); 330 EXPECT_FALSE(data.empty()); 331 delete fetcher_; 332 io_message_loop_proxy()->PostTask(FROM_HERE, new MessageLoop::QuitTask()); 333 } else { 334 // Now running Overload test. 335 static int count = 0; 336 count++; 337 if (count < 20) { 338 fetcher_->Start(); 339 } else { 340 // We have already sent 20 requests continuously. And we expect that 341 // it takes more than 1 second due to the overload pretection settings. 342 EXPECT_TRUE(Time::Now() - start_time_ >= one_second); 343 URLFetcherTest::OnURLFetchComplete(source, url, status, response_code, 344 cookies, data); 345 } 346 } 347} 348 349void URLFetcherProtectTestPassedThrough::CreateFetcher(const GURL& url) { 350 fetcher_ = new URLFetcher(url, URLFetcher::GET, this); 351 fetcher_->set_request_context(new TestURLRequestContextGetter( 352 io_message_loop_proxy())); 353 fetcher_->set_automatically_retry_on_5xx(false); 354 start_time_ = Time::Now(); 355 fetcher_->Start(); 356} 357 358void URLFetcherProtectTestPassedThrough::OnURLFetchComplete( 359 const URLFetcher* source, 360 const GURL& url, 361 const URLRequestStatus& status, 362 int response_code, 363 const ResponseCookies& cookies, 364 const std::string& data) { 365 const TimeDelta one_minute = TimeDelta::FromMilliseconds(60000); 366 if (response_code >= 500) { 367 // Now running ServerUnavailable test. 368 // It should get here on the first attempt, so almost immediately and 369 // *not* to attempt to execute all 11 requests (2.5 minutes). 370 EXPECT_TRUE(Time::Now() - start_time_ < one_minute); 371 EXPECT_TRUE(status.is_success()); 372 // Check that suggested back off time is bigger than 0. 373 EXPECT_GT(fetcher_->backoff_delay().InMicroseconds(), 0); 374 EXPECT_FALSE(data.empty()); 375 } else { 376 // We should not get here! 377 ADD_FAILURE(); 378 } 379 380 delete fetcher_; 381 io_message_loop_proxy()->PostTask(FROM_HERE, new MessageLoop::QuitTask()); 382} 383 384 385URLFetcherBadHTTPSTest::URLFetcherBadHTTPSTest() { 386 PathService::Get(base::DIR_SOURCE_ROOT, &cert_dir_); 387 cert_dir_ = cert_dir_.AppendASCII("chrome"); 388 cert_dir_ = cert_dir_.AppendASCII("test"); 389 cert_dir_ = cert_dir_.AppendASCII("data"); 390 cert_dir_ = cert_dir_.AppendASCII("ssl"); 391 cert_dir_ = cert_dir_.AppendASCII("certificates"); 392} 393 394// The "server certificate expired" error should result in automatic 395// cancellation of the request by 396// URLRequest::Delegate::OnSSLCertificateError. 397void URLFetcherBadHTTPSTest::OnURLFetchComplete( 398 const URLFetcher* source, 399 const GURL& url, 400 const URLRequestStatus& status, 401 int response_code, 402 const ResponseCookies& cookies, 403 const std::string& data) { 404 // This part is different from URLFetcherTest::OnURLFetchComplete 405 // because this test expects the request to be cancelled. 406 EXPECT_EQ(URLRequestStatus::CANCELED, status.status()); 407 EXPECT_EQ(net::ERR_ABORTED, status.os_error()); 408 EXPECT_EQ(-1, response_code); 409 EXPECT_TRUE(cookies.empty()); 410 EXPECT_TRUE(data.empty()); 411 412 // The rest is the same as URLFetcherTest::OnURLFetchComplete. 413 delete fetcher_; 414 io_message_loop_proxy()->PostTask(FROM_HERE, new MessageLoop::QuitTask()); 415} 416 417void URLFetcherCancelTest::CreateFetcher(const GURL& url) { 418 fetcher_ = new URLFetcher(url, URLFetcher::GET, this); 419 CancelTestURLRequestContextGetter* context_getter = 420 new CancelTestURLRequestContextGetter(io_message_loop_proxy()); 421 fetcher_->set_request_context(context_getter); 422 fetcher_->Start(); 423 // We need to wait for the creation of the URLRequestContext, since we 424 // rely on it being destroyed as a signal to end the test. 425 context_getter->WaitForContextCreation(); 426 CancelRequest(); 427} 428 429void URLFetcherCancelTest::OnURLFetchComplete(const URLFetcher* source, 430 const GURL& url, 431 const URLRequestStatus& status, 432 int response_code, 433 const ResponseCookies& cookies, 434 const std::string& data) { 435 // We should have cancelled the request before completion. 436 ADD_FAILURE(); 437 delete fetcher_; 438 io_message_loop_proxy()->PostTask(FROM_HERE, new MessageLoop::QuitTask()); 439} 440 441void URLFetcherCancelTest::CancelRequest() { 442 delete fetcher_; 443 // The URLFetcher's test context will post a Quit task once it is 444 // deleted. So if this test simply hangs, it means cancellation 445 // did not work. 446} 447 448TEST_F(URLFetcherTest, SameThreadsTest) { 449 net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot)); 450 ASSERT_TRUE(test_server.Start()); 451 452 // Create the fetcher on the main thread. Since IO will happen on the main 453 // thread, this will test URLFetcher's ability to do everything on one 454 // thread. 455 CreateFetcher(test_server.GetURL("defaultresponse")); 456 457 MessageLoop::current()->Run(); 458} 459 460TEST_F(URLFetcherTest, DifferentThreadsTest) { 461 net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot)); 462 ASSERT_TRUE(test_server.Start()); 463 464 // Create a separate thread that will create the URLFetcher. The current 465 // (main) thread will do the IO, and when the fetch is complete it will 466 // terminate the main thread's message loop; then the other thread's 467 // message loop will be shut down automatically as the thread goes out of 468 // scope. 469 base::Thread t("URLFetcher test thread"); 470 ASSERT_TRUE(t.Start()); 471 t.message_loop()->PostTask(FROM_HERE, new FetcherWrapperTask(this, 472 test_server.GetURL("defaultresponse"))); 473 474 MessageLoop::current()->Run(); 475} 476 477TEST_F(URLFetcherPostTest, Basic) { 478 net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot)); 479 ASSERT_TRUE(test_server.Start()); 480 481 CreateFetcher(test_server.GetURL("echo")); 482 MessageLoop::current()->Run(); 483} 484 485TEST_F(URLFetcherHeadersTest, Headers) { 486 net::TestServer test_server(net::TestServer::TYPE_HTTP, 487 FilePath(FILE_PATH_LITERAL("net/data/url_request_unittest"))); 488 ASSERT_TRUE(test_server.Start()); 489 490 CreateFetcher(test_server.GetURL("files/with-headers.html")); 491 MessageLoop::current()->Run(); 492 // The actual tests are in the URLFetcherHeadersTest fixture. 493} 494 495TEST_F(URLFetcherProtectTest, Overload) { 496 net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot)); 497 ASSERT_TRUE(test_server.Start()); 498 499 GURL url(test_server.GetURL("defaultresponse")); 500 501 // Registers an entry for test url. It only allows 3 requests to be sent 502 // in 200 milliseconds. 503 URLFetcherProtectManager* manager = URLFetcherProtectManager::GetInstance(); 504 URLFetcherProtectEntry* entry = 505 new URLFetcherProtectEntry(200, 3, 11, 1, 2.0, 0, 256); 506 manager->Register(url.host(), entry); 507 508 CreateFetcher(url); 509 510 MessageLoop::current()->Run(); 511} 512 513TEST_F(URLFetcherProtectTest, ServerUnavailable) { 514 net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot)); 515 ASSERT_TRUE(test_server.Start()); 516 517 GURL url(test_server.GetURL("files/server-unavailable.html")); 518 519 // Registers an entry for test url. The backoff time is calculated by: 520 // new_backoff = 2.0 * old_backoff + 0 521 // and maximum backoff time is 256 milliseconds. 522 // Maximum retries allowed is set to 11. 523 URLFetcherProtectManager* manager = URLFetcherProtectManager::GetInstance(); 524 URLFetcherProtectEntry* entry = 525 new URLFetcherProtectEntry(200, 3, 11, 1, 2.0, 0, 256); 526 manager->Register(url.host(), entry); 527 528 CreateFetcher(url); 529 530 MessageLoop::current()->Run(); 531} 532 533TEST_F(URLFetcherProtectTestPassedThrough, ServerUnavailablePropagateResponse) { 534 net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot)); 535 ASSERT_TRUE(test_server.Start()); 536 537 GURL url(test_server.GetURL("files/server-unavailable.html")); 538 539 // Registers an entry for test url. The backoff time is calculated by: 540 // new_backoff = 2.0 * old_backoff + 0 541 // and maximum backoff time is 256 milliseconds. 542 // Maximum retries allowed is set to 11. 543 URLFetcherProtectManager* manager = URLFetcherProtectManager::GetInstance(); 544 // Total time if *not* for not doing automatic backoff would be 150s. 545 // In reality it should be "as soon as server responds". 546 URLFetcherProtectEntry* entry = 547 new URLFetcherProtectEntry(200, 3, 11, 100, 2.0, 0, 150000); 548 manager->Register(url.host(), entry); 549 550 CreateFetcher(url); 551 552 MessageLoop::current()->Run(); 553} 554 555 556TEST_F(URLFetcherBadHTTPSTest, BadHTTPSTest) { 557 net::TestServer::HTTPSOptions https_options( 558 net::TestServer::HTTPSOptions::CERT_EXPIRED); 559 net::TestServer test_server(https_options, FilePath(kDocRoot)); 560 ASSERT_TRUE(test_server.Start()); 561 562 CreateFetcher(test_server.GetURL("defaultresponse")); 563 MessageLoop::current()->Run(); 564} 565 566TEST_F(URLFetcherCancelTest, ReleasesContext) { 567 net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot)); 568 ASSERT_TRUE(test_server.Start()); 569 570 GURL url(test_server.GetURL("files/server-unavailable.html")); 571 572 // Registers an entry for test url. The backoff time is calculated by: 573 // new_backoff = 2.0 * old_backoff + 0 574 // The initial backoff is 2 seconds and maximum backoff is 4 seconds. 575 // Maximum retries allowed is set to 2. 576 URLFetcherProtectManager* manager = URLFetcherProtectManager::GetInstance(); 577 URLFetcherProtectEntry* entry = 578 new URLFetcherProtectEntry(200, 3, 2, 2000, 2.0, 0, 4000); 579 manager->Register(url.host(), entry); 580 581 // Create a separate thread that will create the URLFetcher. The current 582 // (main) thread will do the IO, and when the fetch is complete it will 583 // terminate the main thread's message loop; then the other thread's 584 // message loop will be shut down automatically as the thread goes out of 585 // scope. 586 base::Thread t("URLFetcher test thread"); 587 ASSERT_TRUE(t.Start()); 588 t.message_loop()->PostTask(FROM_HERE, new FetcherWrapperTask(this, url)); 589 590 MessageLoop::current()->Run(); 591} 592 593TEST_F(URLFetcherCancelTest, CancelWhileDelayedStartTaskPending) { 594 net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot)); 595 ASSERT_TRUE(test_server.Start()); 596 597 GURL url(test_server.GetURL("files/server-unavailable.html")); 598 599 // Register an entry for test url. 600 // 601 // Ideally we would mock URLFetcherProtectEntry to return XXX seconds 602 // in response to entry->UpdateBackoff(SEND). 603 // 604 // Unfortunately this function is time sensitive, so we fudge some numbers 605 // to make it at least somewhat likely to have a non-zero deferred 606 // delay when running. 607 // 608 // Using a sliding window of 2 seconds, and max of 1 request, under a fast 609 // run we expect to have a 4 second delay when posting the Start task. 610 URLFetcherProtectManager* manager = URLFetcherProtectManager::GetInstance(); 611 URLFetcherProtectEntry* entry = 612 new URLFetcherProtectEntry(2000, 1, 2, 2000, 2.0, 0, 4000); 613 EXPECT_EQ(0, entry->UpdateBackoff(URLFetcherProtectEntry::SEND)); 614 entry->UpdateBackoff(URLFetcherProtectEntry::SEND); // Returns about 2000. 615 manager->Register(url.host(), entry); 616 617 // The next request we try to send will be delayed by ~4 seconds. 618 // The slower the test runs, the less the delay will be (since it takes the 619 // time difference from now). 620 621 base::Thread t("URLFetcher test thread"); 622 ASSERT_TRUE(t.Start()); 623 t.message_loop()->PostTask(FROM_HERE, new FetcherWrapperTask(this, url)); 624 625 MessageLoop::current()->Run(); 626} 627 628} // namespace. 629