Index: google_apis/gcm/engine/connection_factory_impl_unittest.cc |
diff --git a/google_apis/gcm/engine/connection_factory_impl_unittest.cc b/google_apis/gcm/engine/connection_factory_impl_unittest.cc |
index eebe14a9b0ad444e44f67975ec13a57293606f84..4983ea9d54b3dfa6ca85f1277eb73c37309a7a93 100644 |
--- a/google_apis/gcm/engine/connection_factory_impl_unittest.cc |
+++ b/google_apis/gcm/engine/connection_factory_impl_unittest.cc |
@@ -9,6 +9,8 @@ |
#include "base/message_loop/message_loop.h" |
#include "base/run_loop.h" |
#include "base/test/simple_test_tick_clock.h" |
+#include "google_apis/gcm/base/mcs_util.h" |
+#include "google_apis/gcm/engine/fake_connection_handler.h" |
#include "net/base/backoff_entry.h" |
#include "net/http/http_network_session.h" |
#include "testing/gtest/include/gtest/gtest.h" |
@@ -70,15 +72,11 @@ double CalculateBackoff(int num_attempts) { |
return delay; |
} |
-// Helper methods that should never actually be called due to real connections |
-// being stubbed out. |
void ReadContinuation( |
scoped_ptr<google::protobuf::MessageLite> message) { |
- ADD_FAILURE(); |
} |
void WriteContinuation() { |
- ADD_FAILURE(); |
} |
class TestBackoffEntry : public net::BackoffEntry { |
@@ -110,11 +108,19 @@ class TestConnectionFactoryImpl : public ConnectionFactoryImpl { |
TestConnectionFactoryImpl(const base::Closure& finished_callback); |
virtual ~TestConnectionFactoryImpl(); |
+ void InitializeFactory(); |
+ |
// Overridden stubs. |
virtual void ConnectImpl() OVERRIDE; |
virtual void InitHandler() OVERRIDE; |
virtual scoped_ptr<net::BackoffEntry> CreateBackoffEntry( |
const net::BackoffEntry::Policy* const policy) OVERRIDE; |
+ virtual scoped_ptr<ConnectionHandler> CreateConnectionHandler( |
+ base::TimeDelta read_timeout, |
+ const ConnectionHandler::ProtoReceivedCallback& read_callback, |
+ const ConnectionHandler::ProtoSentCallback& write_callback, |
+ const ConnectionHandler::ConnectionChangedCallback& connection_callback) |
+ OVERRIDE; |
virtual base::TimeTicks NowTicks() OVERRIDE; |
// Helpers for verifying connection attempts are made. Connection results |
@@ -136,6 +142,8 @@ class TestConnectionFactoryImpl : public ConnectionFactoryImpl { |
bool connections_fulfilled_; |
// Callback to invoke when all connection attempts have been made. |
base::Closure finished_callback_; |
+ // The current fake connection handler.. |
+ FakeConnectionHandler* fake_handler_; |
}; |
TestConnectionFactoryImpl::TestConnectionFactoryImpl( |
@@ -147,7 +155,8 @@ TestConnectionFactoryImpl::TestConnectionFactoryImpl( |
connect_result_(net::ERR_UNEXPECTED), |
num_expected_attempts_(0), |
connections_fulfilled_(true), |
- finished_callback_(finished_callback) { |
+ finished_callback_(finished_callback), |
+ fake_handler_(NULL) { |
// Set a non-null time. |
tick_clock_.Advance(base::TimeDelta::FromMilliseconds(1)); |
} |
@@ -158,7 +167,8 @@ TestConnectionFactoryImpl::~TestConnectionFactoryImpl() { |
void TestConnectionFactoryImpl::ConnectImpl() { |
ASSERT_GT(num_expected_attempts_, 0); |
- |
+ scoped_ptr<mcs_proto::LoginRequest> request(BuildLoginRequest(0, 0, "")); |
+ GetConnectionHandler()->Init(*request, NULL); |
OnConnectDone(connect_result_); |
if (!NextRetryAttempt().is_null()) { |
// Advance the time to the next retry time. |
@@ -184,6 +194,18 @@ scoped_ptr<net::BackoffEntry> TestConnectionFactoryImpl::CreateBackoffEntry( |
return scoped_ptr<net::BackoffEntry>(new TestBackoffEntry(&tick_clock_)); |
} |
+scoped_ptr<ConnectionHandler> |
+TestConnectionFactoryImpl::CreateConnectionHandler( |
+ base::TimeDelta read_timeout, |
+ const ConnectionHandler::ProtoReceivedCallback& read_callback, |
+ const ConnectionHandler::ProtoSentCallback& write_callback, |
+ const ConnectionHandler::ConnectionChangedCallback& connection_callback) { |
+ fake_handler_ = new FakeConnectionHandler( |
+ base::Bind(&ReadContinuation), |
+ base::Bind(&WriteContinuation)); |
+ return make_scoped_ptr<ConnectionHandler>(fake_handler_); |
+} |
+ |
base::TimeTicks TestConnectionFactoryImpl::NowTicks() { |
return tick_clock_.NowTicks(); |
} |
@@ -194,6 +216,10 @@ void TestConnectionFactoryImpl::SetConnectResult(int connect_result) { |
connections_fulfilled_ = false; |
connect_result_ = connect_result; |
num_expected_attempts_ = 1; |
+ fake_handler_->ExpectOutgoingMessage( |
+ MCSMessage(kLoginRequestTag, |
+ BuildLoginRequest(0, 0, "").PassAs< |
+ const google::protobuf::MessageLite>())); |
} |
void TestConnectionFactoryImpl::SetMultipleConnectResults( |
@@ -205,6 +231,12 @@ void TestConnectionFactoryImpl::SetMultipleConnectResults( |
connections_fulfilled_ = false; |
connect_result_ = connect_result; |
num_expected_attempts_ = num_expected_attempts; |
+ for (int i = 0 ; i < num_expected_attempts; ++i) { |
+ fake_handler_->ExpectOutgoingMessage( |
+ MCSMessage(kLoginRequestTag, |
+ BuildLoginRequest(0, 0, "").PassAs< |
+ const google::protobuf::MessageLite>())); |
+ } |
} |
class ConnectionFactoryImplTest : public testing::Test { |
@@ -227,7 +259,12 @@ class ConnectionFactoryImplTest : public testing::Test { |
ConnectionFactoryImplTest::ConnectionFactoryImplTest() |
: factory_(base::Bind(&ConnectionFactoryImplTest::ConnectionsComplete, |
base::Unretained(this))), |
- run_loop_(new base::RunLoop()) {} |
+ run_loop_(new base::RunLoop()) { |
+ factory()->Initialize( |
+ ConnectionFactory::BuildLoginRequestCallback(), |
+ ConnectionHandler::ProtoReceivedCallback(), |
+ ConnectionHandler::ProtoSentCallback()); |
+} |
ConnectionFactoryImplTest::~ConnectionFactoryImplTest() {} |
void ConnectionFactoryImplTest::WaitForConnections() { |
@@ -243,11 +280,6 @@ void ConnectionFactoryImplTest::ConnectionsComplete() { |
// Verify building a connection handler works. |
TEST_F(ConnectionFactoryImplTest, Initialize) { |
- EXPECT_FALSE(factory()->IsEndpointReachable()); |
- factory()->Initialize( |
- ConnectionFactory::BuildLoginRequestCallback(), |
- base::Bind(&ReadContinuation), |
- base::Bind(&WriteContinuation)); |
ConnectionHandler* handler = factory()->GetConnectionHandler(); |
ASSERT_TRUE(handler); |
EXPECT_FALSE(factory()->IsEndpointReachable()); |
@@ -255,54 +287,42 @@ TEST_F(ConnectionFactoryImplTest, Initialize) { |
// An initial successful connection should not result in backoff. |
TEST_F(ConnectionFactoryImplTest, ConnectSuccess) { |
- factory()->Initialize( |
- ConnectionFactory::BuildLoginRequestCallback(), |
- ConnectionHandler::ProtoReceivedCallback(), |
- ConnectionHandler::ProtoSentCallback()); |
factory()->SetConnectResult(net::OK); |
factory()->Connect(); |
EXPECT_TRUE(factory()->NextRetryAttempt().is_null()); |
EXPECT_EQ(factory()->GetCurrentEndpoint(), BuildEndpoints()[0]); |
+ EXPECT_TRUE(factory()->IsEndpointReachable()); |
} |
// A connection failure should result in backoff, and attempting the fallback |
// endpoint next. |
TEST_F(ConnectionFactoryImplTest, ConnectFail) { |
- factory()->Initialize( |
- ConnectionFactory::BuildLoginRequestCallback(), |
- ConnectionHandler::ProtoReceivedCallback(), |
- ConnectionHandler::ProtoSentCallback()); |
factory()->SetConnectResult(net::ERR_CONNECTION_FAILED); |
factory()->Connect(); |
EXPECT_FALSE(factory()->NextRetryAttempt().is_null()); |
EXPECT_EQ(factory()->GetCurrentEndpoint(), BuildEndpoints()[1]); |
+ EXPECT_FALSE(factory()->IsEndpointReachable()); |
} |
// A connection success after a failure should reset backoff. |
TEST_F(ConnectionFactoryImplTest, FailThenSucceed) { |
- factory()->Initialize( |
- ConnectionFactory::BuildLoginRequestCallback(), |
- ConnectionHandler::ProtoReceivedCallback(), |
- ConnectionHandler::ProtoSentCallback()); |
factory()->SetConnectResult(net::ERR_CONNECTION_FAILED); |
base::TimeTicks connect_time = factory()->tick_clock()->NowTicks(); |
factory()->Connect(); |
WaitForConnections(); |
+ EXPECT_FALSE(factory()->IsEndpointReachable()); |
base::TimeTicks retry_time = factory()->NextRetryAttempt(); |
EXPECT_FALSE(retry_time.is_null()); |
EXPECT_GE((retry_time - connect_time).InMilliseconds(), CalculateBackoff(1)); |
factory()->SetConnectResult(net::OK); |
WaitForConnections(); |
EXPECT_TRUE(factory()->NextRetryAttempt().is_null()); |
+ EXPECT_TRUE(factory()->IsEndpointReachable()); |
} |
// Multiple connection failures should retry with an exponentially increasing |
// backoff, then reset on success. |
TEST_F(ConnectionFactoryImplTest, MultipleFailuresThenSucceed) { |
- factory()->Initialize( |
- ConnectionFactory::BuildLoginRequestCallback(), |
- ConnectionHandler::ProtoReceivedCallback(), |
- ConnectionHandler::ProtoSentCallback()); |
const int kNumAttempts = 5; |
factory()->SetMultipleConnectResults(net::ERR_CONNECTION_FAILED, |
@@ -311,6 +331,7 @@ TEST_F(ConnectionFactoryImplTest, MultipleFailuresThenSucceed) { |
base::TimeTicks connect_time = factory()->tick_clock()->NowTicks(); |
factory()->Connect(); |
WaitForConnections(); |
+ EXPECT_FALSE(factory()->IsEndpointReachable()); |
base::TimeTicks retry_time = factory()->NextRetryAttempt(); |
EXPECT_FALSE(retry_time.is_null()); |
EXPECT_GE((retry_time - connect_time).InMilliseconds(), |
@@ -319,59 +340,80 @@ TEST_F(ConnectionFactoryImplTest, MultipleFailuresThenSucceed) { |
factory()->SetConnectResult(net::OK); |
WaitForConnections(); |
EXPECT_TRUE(factory()->NextRetryAttempt().is_null()); |
+ EXPECT_TRUE(factory()->IsEndpointReachable()); |
} |
-// IP events should reset backoff. |
+// IP events should trigger canary connections. |
TEST_F(ConnectionFactoryImplTest, FailThenIPEvent) { |
- factory()->Initialize( |
- ConnectionFactory::BuildLoginRequestCallback(), |
- ConnectionHandler::ProtoReceivedCallback(), |
- ConnectionHandler::ProtoSentCallback()); |
factory()->SetConnectResult(net::ERR_CONNECTION_FAILED); |
factory()->Connect(); |
WaitForConnections(); |
- EXPECT_FALSE(factory()->NextRetryAttempt().is_null()); |
+ base::TimeTicks initial_backoff = factory()->NextRetryAttempt(); |
+ EXPECT_FALSE(initial_backoff.is_null()); |
+ factory()->SetConnectResult(net::ERR_FAILED); |
factory()->OnIPAddressChanged(); |
- EXPECT_TRUE(factory()->NextRetryAttempt().is_null()); |
+ WaitForConnections(); |
+ |
+ // Backoff should increase. |
+ base::TimeTicks next_backoff = factory()->NextRetryAttempt(); |
+ EXPECT_GT(next_backoff, initial_backoff); |
+ EXPECT_FALSE(factory()->IsEndpointReachable()); |
} |
-// Connection type events should reset backoff. |
+// Connection type events should trigger canary connections. |
TEST_F(ConnectionFactoryImplTest, FailThenConnectionTypeEvent) { |
- factory()->Initialize( |
- ConnectionFactory::BuildLoginRequestCallback(), |
- ConnectionHandler::ProtoReceivedCallback(), |
- ConnectionHandler::ProtoSentCallback()); |
factory()->SetConnectResult(net::ERR_CONNECTION_FAILED); |
factory()->Connect(); |
WaitForConnections(); |
- EXPECT_FALSE(factory()->NextRetryAttempt().is_null()); |
+ base::TimeTicks initial_backoff = factory()->NextRetryAttempt(); |
+ EXPECT_FALSE(initial_backoff.is_null()); |
+ factory()->SetConnectResult(net::ERR_FAILED); |
factory()->OnConnectionTypeChanged( |
net::NetworkChangeNotifier::CONNECTION_WIFI); |
- EXPECT_TRUE(factory()->NextRetryAttempt().is_null()); |
+ WaitForConnections(); |
+ |
+ // Backoff should increase. |
+ base::TimeTicks next_backoff = factory()->NextRetryAttempt(); |
+ EXPECT_GT(next_backoff, initial_backoff); |
+ EXPECT_FALSE(factory()->IsEndpointReachable()); |
+} |
+ |
+// Verify that we reconnect even if a canary succeeded then disconnected while |
+// a backoff was pending. |
+TEST_F(ConnectionFactoryImplTest, CanarySucceedsThenDisconnects) { |
+ factory()->SetConnectResult(net::ERR_CONNECTION_FAILED); |
+ factory()->Connect(); |
+ WaitForConnections(); |
+ base::TimeTicks initial_backoff = factory()->NextRetryAttempt(); |
+ EXPECT_FALSE(initial_backoff.is_null()); |
+ |
+ factory()->SetConnectResult(net::OK); |
+ factory()->OnConnectionTypeChanged( |
+ net::NetworkChangeNotifier::CONNECTION_WIFI); |
+ WaitForConnections(); |
+ EXPECT_TRUE(factory()->IsEndpointReachable()); |
+ |
+ factory()->SetConnectResult(net::OK); |
+ factory()->SignalConnectionReset(ConnectionFactory::SOCKET_FAILURE); |
+ EXPECT_FALSE(factory()->IsEndpointReachable()); |
+ WaitForConnections(); |
+ EXPECT_TRUE(factory()->IsEndpointReachable()); |
} |
// Fail after successful connection via signal reset. |
TEST_F(ConnectionFactoryImplTest, FailViaSignalReset) { |
- factory()->Initialize( |
- ConnectionFactory::BuildLoginRequestCallback(), |
- ConnectionHandler::ProtoReceivedCallback(), |
- ConnectionHandler::ProtoSentCallback()); |
factory()->SetConnectResult(net::OK); |
factory()->Connect(); |
EXPECT_TRUE(factory()->NextRetryAttempt().is_null()); |
factory()->SignalConnectionReset(ConnectionFactory::SOCKET_FAILURE); |
EXPECT_FALSE(factory()->NextRetryAttempt().is_null()); |
- EXPECT_FALSE(factory()->GetConnectionHandler()->CanSendMessage()); |
+ EXPECT_FALSE(factory()->IsEndpointReachable()); |
} |
TEST_F(ConnectionFactoryImplTest, IgnoreResetWhileConnecting) { |
- factory()->Initialize( |
- ConnectionFactory::BuildLoginRequestCallback(), |
- ConnectionHandler::ProtoReceivedCallback(), |
- ConnectionHandler::ProtoSentCallback()); |
factory()->SetConnectResult(net::OK); |
factory()->Connect(); |
EXPECT_TRUE(factory()->NextRetryAttempt().is_null()); |
@@ -379,21 +421,19 @@ TEST_F(ConnectionFactoryImplTest, IgnoreResetWhileConnecting) { |
factory()->SignalConnectionReset(ConnectionFactory::SOCKET_FAILURE); |
base::TimeTicks retry_time = factory()->NextRetryAttempt(); |
EXPECT_FALSE(retry_time.is_null()); |
+ EXPECT_FALSE(factory()->IsEndpointReachable()); |
const int kNumAttempts = 5; |
for (int i = 0; i < kNumAttempts; ++i) |
factory()->SignalConnectionReset(ConnectionFactory::SOCKET_FAILURE); |
EXPECT_EQ(retry_time, factory()->NextRetryAttempt()); |
+ EXPECT_FALSE(factory()->IsEndpointReachable()); |
} |
// Go into backoff due to connection failure. On successful connection, receive |
// a signal reset. The original backoff should be restored and extended, rather |
// than a new backoff starting from scratch. |
TEST_F(ConnectionFactoryImplTest, SignalResetRestoresBackoff) { |
- factory()->Initialize( |
- ConnectionFactory::BuildLoginRequestCallback(), |
- ConnectionHandler::ProtoReceivedCallback(), |
- ConnectionHandler::ProtoSentCallback()); |
factory()->SetConnectResult(net::ERR_CONNECTION_FAILED); |
base::TimeTicks connect_time = factory()->tick_clock()->NowTicks(); |
factory()->Connect(); |
@@ -407,7 +447,7 @@ TEST_F(ConnectionFactoryImplTest, SignalResetRestoresBackoff) { |
EXPECT_TRUE(factory()->NextRetryAttempt().is_null()); |
factory()->SignalConnectionReset(ConnectionFactory::SOCKET_FAILURE); |
- EXPECT_FALSE(factory()->GetConnectionHandler()->CanSendMessage()); |
+ EXPECT_FALSE(factory()->IsEndpointReachable()); |
EXPECT_NE(retry_time, factory()->NextRetryAttempt()); |
retry_time = factory()->NextRetryAttempt(); |
EXPECT_FALSE(retry_time.is_null()); |
@@ -420,6 +460,7 @@ TEST_F(ConnectionFactoryImplTest, SignalResetRestoresBackoff) { |
factory()->NextRetryAttempt() - connect_time); |
WaitForConnections(); |
EXPECT_TRUE(factory()->NextRetryAttempt().is_null()); |
+ EXPECT_TRUE(factory()->IsEndpointReachable()); |
factory()->SignalConnectionReset(ConnectionFactory::SOCKET_FAILURE); |
EXPECT_NE(retry_time, factory()->NextRetryAttempt()); |
@@ -427,6 +468,7 @@ TEST_F(ConnectionFactoryImplTest, SignalResetRestoresBackoff) { |
EXPECT_FALSE(retry_time.is_null()); |
EXPECT_GE((retry_time - connect_time).InMilliseconds(), |
CalculateBackoff(3)); |
+ EXPECT_FALSE(factory()->IsEndpointReachable()); |
} |
} // namespace |