Chromium Code Reviews| Index: net/url_request/url_fetcher_impl_unittest.cc |
| diff --git a/net/url_request/url_fetcher_impl_unittest.cc b/net/url_request/url_fetcher_impl_unittest.cc |
| index 1c6b77b71f7143ec3a7bbdb4a671a16b394893bd..7054c7b2cfe190d1c43a5e8f5bcf0391d9b0a2e8 100644 |
| --- a/net/url_request/url_fetcher_impl_unittest.cc |
| +++ b/net/url_request/url_fetcher_impl_unittest.cc |
| @@ -10,10 +10,13 @@ |
| #include "base/file_util.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/message_loop_proxy.h" |
| +#include "base/stringprintf.h" |
| #include "base/synchronization/waitable_event.h" |
| #include "base/threading/thread.h" |
| #include "build/build_config.h" |
| #include "crypto/nss_util.h" |
| +#include "net/base/mock_host_resolver.h" |
| +#include "net/base/network_change_notifier.h" |
| #include "net/http/http_response_headers.h" |
| #include "net/test/test_server.h" |
| #include "net/url_request/url_fetcher_delegate.h" |
| @@ -72,6 +75,24 @@ class ThrottlingTestURLRequestContextGetter |
| TestURLRequestContext* const context_; |
| }; |
| +class TestNetworkChangeNotifier : public NetworkChangeNotifier { |
| + public: |
| + TestNetworkChangeNotifier() : connection_type_(CONNECTION_UNKNOWN) {} |
| + |
| + // Implementation of NetworkChangeNotifier: |
| + virtual ConnectionType GetCurrentConnectionType() const OVERRIDE { |
| + return connection_type_; |
| + } |
| + |
| + void SetCurrentConnectionType(ConnectionType type) { |
| + connection_type_ = type; |
| + NotifyObserversOfConnectionTypeChange(); |
| + } |
| + |
| + private: |
| + ConnectionType connection_type_; |
| +}; |
| + |
| } // namespace |
| class URLFetcherTest : public testing::Test, |
| @@ -79,7 +100,7 @@ class URLFetcherTest : public testing::Test, |
| public: |
| URLFetcherTest() |
| : fetcher_(NULL), |
| - context_(new ThrottlingTestURLRequestContext()) { |
| + context_(NULL) { |
| } |
| static int GetNumFetcherCores() { |
| @@ -111,6 +132,7 @@ class URLFetcherTest : public testing::Test, |
| virtual void SetUp() OVERRIDE { |
| testing::Test::SetUp(); |
| + context_.reset(new ThrottlingTestURLRequestContext()); |
| io_message_loop_proxy_ = base::MessageLoopProxy::current(); |
| #if defined(USE_NSS) || defined(OS_IOS) |
| @@ -132,7 +154,29 @@ class URLFetcherTest : public testing::Test, |
| scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_; |
| URLFetcherImpl* fetcher_; |
| - const scoped_ptr<TestURLRequestContext> context_; |
| + scoped_ptr<TestURLRequestContext> context_; |
| +}; |
| + |
| +// A test fixture that uses a MockHostResolver, so that name resolutions can |
| +// be manipulated by the tests to keep connections in the resolving state. |
| +class URLFetcherMockDNSTest : public URLFetcherTest { |
|
szym
2012/12/12 19:50:46
nit: Suggest 'Dns' (like 'Http' and most code in n
Joao da Silva
2012/12/12 20:17:20
Done.
|
| + public: |
| + // testing::Test: |
| + virtual void SetUp() OVERRIDE; |
| + |
| + // URLFetcherTest: |
| + virtual void CreateFetcher(const GURL& url) OVERRIDE; |
| + |
| + // URLFetcherDelegate: |
| + virtual void OnURLFetchComplete(const URLFetcher* source) OVERRIDE; |
| + |
| + protected: |
| + GURL test_url_; |
| + scoped_ptr<TestServer> test_server_; |
| + MockHostResolver resolver_; |
| + scoped_ptr<URLFetcher> completed_fetcher_; |
| + NetworkChangeNotifier::DisableForTest disable_default_notifier_; |
| + TestNetworkChangeNotifier network_change_notifier_; |
| }; |
| void URLFetcherTest::CreateFetcher(const GURL& url) { |
| @@ -163,6 +207,43 @@ void URLFetcherTest::CleanupAfterFetchComplete() { |
| // the main loop returns and this thread subsequently goes out of scope. |
| } |
| +void URLFetcherMockDNSTest::SetUp() { |
| + URLFetcherTest::SetUp(); |
| + |
| + resolver_.set_ondemand_mode(true); |
| + resolver_.rules()->AddRule("example.com", "127.0.0.1"); |
| + |
| + context_.reset(new TestURLRequestContext(true)); |
| + context_->set_host_resolver(&resolver_); |
| + context_->Init(); |
| + |
| + test_server_.reset(new TestServer(TestServer::TYPE_HTTP, |
| + TestServer::kLocalhost, |
| + FilePath(kDocRoot))); |
| + ASSERT_TRUE(test_server_->Start()); |
| + |
| + // test_server_.GetURL() returns a URL with 127.0.0.1 (kLocalhost), that is |
| + // immediately resolved by the MockHostResolver. Use a hostname instead to |
| + // trigger an async resolve. |
| + test_url_ = GURL( |
| + base::StringPrintf("http://example.com:%d/defaultresponse", |
| + test_server_->host_port_pair().port())); |
| + ASSERT_TRUE(test_url_.is_valid()); |
| +} |
| + |
| +void URLFetcherMockDNSTest::CreateFetcher(const GURL& url) { |
| + fetcher_ = new URLFetcherImpl(url, URLFetcher::GET, this); |
| + fetcher_->SetRequestContext(new ThrottlingTestURLRequestContextGetter( |
| + io_message_loop_proxy(), request_context())); |
| +} |
| + |
| +void URLFetcherMockDNSTest::OnURLFetchComplete(const URLFetcher* source) { |
| + io_message_loop_proxy()->PostTask(FROM_HERE, MessageLoop::QuitClosure()); |
| + ASSERT_EQ(fetcher_, source); |
| + EXPECT_EQ(test_url_, source->GetOriginalURL()); |
| + completed_fetcher_.reset(fetcher_); |
| +} |
| + |
| namespace { |
| // Version of URLFetcherTest that does a POST instead |
| @@ -584,7 +665,7 @@ void URLFetcherProtectTest::CreateFetcher(const GURL& url) { |
| fetcher_->SetRequestContext(new ThrottlingTestURLRequestContextGetter( |
| io_message_loop_proxy(), request_context())); |
| start_time_ = Time::Now(); |
| - fetcher_->SetMaxRetries(11); |
| + fetcher_->SetMaxRetriesOn5xx(11); |
| fetcher_->Start(); |
| } |
| @@ -623,7 +704,7 @@ void URLFetcherProtectTestPassedThrough::CreateFetcher(const GURL& url) { |
| io_message_loop_proxy(), request_context())); |
| fetcher_->SetAutomaticallyRetryOn5xx(false); |
| start_time_ = Time::Now(); |
| - fetcher_->SetMaxRetries(11); |
| + fetcher_->SetMaxRetriesOn5xx(11); |
| fetcher_->Start(); |
| } |
| @@ -682,7 +763,7 @@ void URLFetcherCancelTest::CreateFetcher(const GURL& url) { |
| new CancelTestURLRequestContextGetter(io_message_loop_proxy(), |
| url); |
| fetcher_->SetRequestContext(context_getter); |
| - fetcher_->SetMaxRetries(2); |
| + fetcher_->SetMaxRetriesOn5xx(2); |
| fetcher_->Start(); |
| // We need to wait for the creation of the URLRequestContext, since we |
| // rely on it being destroyed as a signal to end the test. |
| @@ -828,6 +909,205 @@ TEST_F(URLFetcherTest, CancelAll) { |
| delete fetcher_; |
| } |
| +TEST_F(URLFetcherMockDNSTest, DontRetryOnNetworkChangedByDefault) { |
| + EXPECT_EQ(0, GetNumFetcherCores()); |
| + EXPECT_FALSE(resolver_.has_pending_requests()); |
| + |
| + // This posts a task to start the fetcher. |
| + CreateFetcher(test_url_); |
| + fetcher_->Start(); |
| + EXPECT_EQ(0, GetNumFetcherCores()); |
| + MessageLoop::current()->RunUntilIdle(); |
| + |
| + // The fetcher is now running, but is pending the host resolve. |
| + EXPECT_EQ(1, GetNumFetcherCores()); |
| + EXPECT_TRUE(resolver_.has_pending_requests()); |
| + ASSERT_FALSE(completed_fetcher_); |
| + |
| + // A network change notification aborts the connect job. |
| + NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests(); |
| + MessageLoop::current()->RunUntilIdle(); |
| + EXPECT_EQ(0, GetNumFetcherCores()); |
| + EXPECT_FALSE(resolver_.has_pending_requests()); |
| + ASSERT_TRUE(completed_fetcher_); |
| + |
| + // And the owner of the fetcher gets the ERR_NETWORK_CHANGED error. |
| + EXPECT_EQ(ERR_NETWORK_CHANGED, completed_fetcher_->GetStatus().error()); |
| +} |
| + |
| +TEST_F(URLFetcherMockDNSTest, RetryOnNetworkChangedAndFail) { |
| + EXPECT_EQ(0, GetNumFetcherCores()); |
| + EXPECT_FALSE(resolver_.has_pending_requests()); |
| + |
| + // This posts a task to start the fetcher. |
| + CreateFetcher(test_url_); |
| + fetcher_->SetAutomaticallyRetryOnNetworkChanges(3); |
| + fetcher_->Start(); |
| + EXPECT_EQ(0, GetNumFetcherCores()); |
| + MessageLoop::current()->RunUntilIdle(); |
| + |
| + // The fetcher is now running, but is pending the host resolve. |
| + EXPECT_EQ(1, GetNumFetcherCores()); |
| + EXPECT_TRUE(resolver_.has_pending_requests()); |
| + ASSERT_FALSE(completed_fetcher_); |
| + |
| + // Make it fail 3 times. |
| + for (int i = 0; i < 3; ++i) { |
| + // A network change notification aborts the connect job. |
| + NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests(); |
| + MessageLoop::current()->RunUntilIdle(); |
| + |
| + // But the fetcher retries automatically. |
| + EXPECT_EQ(1, GetNumFetcherCores()); |
| + EXPECT_TRUE(resolver_.has_pending_requests()); |
| + ASSERT_FALSE(completed_fetcher_); |
| + } |
| + |
| + // A 4th failure doesn't trigger another retry, and propagates the error |
| + // to the owner of the fetcher. |
| + NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests(); |
| + MessageLoop::current()->RunUntilIdle(); |
| + EXPECT_EQ(0, GetNumFetcherCores()); |
| + EXPECT_FALSE(resolver_.has_pending_requests()); |
| + ASSERT_TRUE(completed_fetcher_); |
| + |
| + // And the owner of the fetcher gets the ERR_NETWORK_CHANGED error. |
| + EXPECT_EQ(ERR_NETWORK_CHANGED, completed_fetcher_->GetStatus().error()); |
| +} |
| + |
| +TEST_F(URLFetcherMockDNSTest, RetryOnNetworkChangedAndSucceed) { |
| + EXPECT_EQ(0, GetNumFetcherCores()); |
| + EXPECT_FALSE(resolver_.has_pending_requests()); |
| + |
| + // This posts a task to start the fetcher. |
| + CreateFetcher(test_url_); |
| + fetcher_->SetAutomaticallyRetryOnNetworkChanges(3); |
| + fetcher_->Start(); |
| + EXPECT_EQ(0, GetNumFetcherCores()); |
| + MessageLoop::current()->RunUntilIdle(); |
| + |
| + // The fetcher is now running, but is pending the host resolve. |
| + EXPECT_EQ(1, GetNumFetcherCores()); |
| + EXPECT_TRUE(resolver_.has_pending_requests()); |
| + ASSERT_FALSE(completed_fetcher_); |
| + |
| + // Make it fail 3 times. |
| + for (int i = 0; i < 3; ++i) { |
| + // A network change notification aborts the connect job. |
| + NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests(); |
| + MessageLoop::current()->RunUntilIdle(); |
| + |
| + // But the fetcher retries automatically. |
| + EXPECT_EQ(1, GetNumFetcherCores()); |
| + EXPECT_TRUE(resolver_.has_pending_requests()); |
| + ASSERT_FALSE(completed_fetcher_); |
| + } |
| + |
| + // Now let it succeed by resolving the pending request. |
| + resolver_.ResolveAllPending(); |
| + MessageLoop::current()->Run(); |
| + |
| + // URLFetcherMockDNSTest::OnURLFetchComplete() will quit the loop. |
| + EXPECT_EQ(0, GetNumFetcherCores()); |
| + EXPECT_FALSE(resolver_.has_pending_requests()); |
| + ASSERT_TRUE(completed_fetcher_); |
| + |
| + // This time the request succeeded. |
| + EXPECT_EQ(OK, completed_fetcher_->GetStatus().error()); |
| + EXPECT_EQ(200, completed_fetcher_->GetResponseCode()); |
| +} |
| + |
| +TEST_F(URLFetcherMockDNSTest, RetryAfterComingBackOnline) { |
| + EXPECT_EQ(0, GetNumFetcherCores()); |
| + EXPECT_FALSE(resolver_.has_pending_requests()); |
| + |
| + // This posts a task to start the fetcher. |
| + CreateFetcher(test_url_); |
| + fetcher_->SetAutomaticallyRetryOnNetworkChanges(1); |
| + fetcher_->Start(); |
| + EXPECT_EQ(0, GetNumFetcherCores()); |
| + MessageLoop::current()->RunUntilIdle(); |
| + |
| + // The fetcher is now running, but is pending the host resolve. |
| + EXPECT_EQ(1, GetNumFetcherCores()); |
| + EXPECT_TRUE(resolver_.has_pending_requests()); |
| + ASSERT_FALSE(completed_fetcher_); |
| + |
| + // Make it fail by changing the connection type to offline. |
| + EXPECT_EQ(NetworkChangeNotifier::CONNECTION_UNKNOWN, |
| + NetworkChangeNotifier::GetConnectionType()); |
| + EXPECT_FALSE(NetworkChangeNotifier::IsOffline()); |
| + network_change_notifier_.SetCurrentConnectionType( |
| + NetworkChangeNotifier::CONNECTION_NONE); |
| + // This makes the connect job fail: |
| + NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests(); |
| + resolver_.ResolveAllPending(); |
| + MessageLoop::current()->RunUntilIdle(); |
| + |
| + // The fetcher is now waiting for the connection to become online again. |
| + EXPECT_EQ(NetworkChangeNotifier::CONNECTION_NONE, |
| + NetworkChangeNotifier::GetConnectionType()); |
| + EXPECT_TRUE(NetworkChangeNotifier::IsOffline()); |
| + EXPECT_FALSE(resolver_.has_pending_requests()); |
| + ASSERT_FALSE(completed_fetcher_); |
| + // The core is still alive, but it dropped its request. |
| + EXPECT_EQ(0, GetNumFetcherCores()); |
| + |
| + // It should retry once the connection is back. |
| + network_change_notifier_.SetCurrentConnectionType( |
| + NetworkChangeNotifier::CONNECTION_WIFI); |
| + MessageLoop::current()->RunUntilIdle(); |
| + EXPECT_EQ(1, GetNumFetcherCores()); |
| + ASSERT_FALSE(completed_fetcher_); |
| + EXPECT_TRUE(resolver_.has_pending_requests()); |
| + |
| + // Resolve the pending request; the fetcher should complete now. |
| + resolver_.ResolveAllPending(); |
| + MessageLoop::current()->Run(); |
| + |
| + EXPECT_EQ(0, GetNumFetcherCores()); |
| + EXPECT_FALSE(resolver_.has_pending_requests()); |
| + ASSERT_TRUE(completed_fetcher_); |
| + EXPECT_EQ(OK, completed_fetcher_->GetStatus().error()); |
| + EXPECT_EQ(200, completed_fetcher_->GetResponseCode()); |
| +} |
| + |
| +TEST_F(URLFetcherMockDNSTest, StartOnlyWhenOnline) { |
| + // Start offline. |
| + network_change_notifier_.SetCurrentConnectionType( |
| + NetworkChangeNotifier::CONNECTION_NONE); |
| + EXPECT_TRUE(NetworkChangeNotifier::IsOffline()); |
| + EXPECT_EQ(0, GetNumFetcherCores()); |
| + EXPECT_FALSE(resolver_.has_pending_requests()); |
| + |
| + // Create a fetcher that retries on network changes. It will try to connect |
| + // only once the network is back online. |
| + CreateFetcher(test_url_); |
| + fetcher_->SetAutomaticallyRetryOnNetworkChanges(1); |
| + fetcher_->Start(); |
| + MessageLoop::current()->RunUntilIdle(); |
| + EXPECT_EQ(0, GetNumFetcherCores()); |
| + EXPECT_FALSE(resolver_.has_pending_requests()); |
| + |
| + // It should retry once the connection is back. |
| + network_change_notifier_.SetCurrentConnectionType( |
| + NetworkChangeNotifier::CONNECTION_WIFI); |
| + MessageLoop::current()->RunUntilIdle(); |
| + EXPECT_EQ(1, GetNumFetcherCores()); |
| + ASSERT_FALSE(completed_fetcher_); |
| + EXPECT_TRUE(resolver_.has_pending_requests()); |
| + |
| + // Resolve the pending request; the fetcher should complete now. |
| + resolver_.ResolveAllPending(); |
| + MessageLoop::current()->Run(); |
| + |
| + EXPECT_EQ(0, GetNumFetcherCores()); |
| + EXPECT_FALSE(resolver_.has_pending_requests()); |
| + ASSERT_TRUE(completed_fetcher_); |
| + EXPECT_EQ(OK, completed_fetcher_->GetStatus().error()); |
| + EXPECT_EQ(200, completed_fetcher_->GetResponseCode()); |
| +} |
| + |
| #if defined(OS_MACOSX) |
| // SIGSEGV on Mac: http://crbug.com/60426 |
| TEST_F(URLFetcherPostTest, DISABLED_Basic) { |