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..e800a7b9fc2af8e098212f9ce39da442c65da1b3 100644 |
--- a/net/url_request/url_fetcher_impl_unittest.cc |
+++ b/net/url_request/url_fetcher_impl_unittest.cc |
@@ -10,16 +10,20 @@ |
#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" |
#include "net/url_request/url_request_context_getter.h" |
#include "net/url_request/url_request_test_util.h" |
#include "net/url_request/url_request_throttler_manager.h" |
+#include "testing/gmock/include/gmock/gmock.h" |
szym
2012/12/10 18:36:31
Not necessary.
Joao da Silva
2012/12/11 13:36:43
Done.
|
#include "testing/gtest/include/gtest/gtest.h" |
#if defined(USE_NSS) || defined(OS_IOS) |
@@ -72,6 +76,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 +101,7 @@ class URLFetcherTest : public testing::Test, |
public: |
URLFetcherTest() |
: fetcher_(NULL), |
- context_(new ThrottlingTestURLRequestContext()) { |
+ context_(NULL) { |
} |
static int GetNumFetcherCores() { |
@@ -111,6 +133,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 +155,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 { |
+ 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 +208,44 @@ void URLFetcherTest::CleanupAfterFetchComplete() { |
// the main loop returns and this thread subsequently goes out of scope. |
} |
+void URLFetcherMockDNSTest::SetUp() { |
+ URLFetcherTest::SetUp(); |
+ |
+ resolver_.set_synchronous_mode(false); |
+ resolver_.set_resolve_automatically(false); |
+ 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 +667,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 +706,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 +765,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 +911,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) { |