Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(139)

Unified Diff: net/url_request/url_fetcher_impl_unittest.cc

Issue 11464028: Introduce ERR_NETWORK_CHANGED and allow URLFetcher to automatically retry on that error. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebased, post first try if online, more tests Created 8 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« net/url_request/url_fetcher_core.cc ('K') | « net/url_request/url_fetcher_impl.cc ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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) {
« net/url_request/url_fetcher_core.cc ('K') | « net/url_request/url_fetcher_impl.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698