Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2013 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 "google_apis/gcm/engine/connection_factory_impl.h" | |
| 6 | |
| 7 #include "base/message_loop/message_loop.h" | |
| 8 #include "base/run_loop.h" | |
| 9 #include "base/time/time.h" | |
| 10 #include "net/base/backoff_entry.h" | |
| 11 #include "net/http/http_network_session.h" | |
| 12 #include "testing/gtest/include/gtest/gtest.h" | |
| 13 | |
| 14 class Policy; | |
| 15 | |
| 16 namespace gcm { | |
| 17 namespace { | |
| 18 | |
| 19 const char kMCSEndpoint[] = "http://my.server"; | |
| 20 | |
| 21 const int kBackoffDelayMs = 1; | |
| 22 const int kBackoffMultiplier = 2; | |
| 23 | |
| 24 // A backoff policy with small enough delays that tests aren't burdened. | |
| 25 const net::BackoffEntry::Policy kTestBackoffPolicy = { | |
| 26 // Number of initial errors (in sequence) to ignore before applying | |
| 27 // exponential back-off rules. | |
| 28 0, | |
| 29 | |
| 30 // Initial delay for exponential back-off in ms. | |
| 31 kBackoffDelayMs, | |
| 32 | |
| 33 // Factor by which the waiting time will be multiplied. | |
| 34 kBackoffMultiplier, | |
| 35 | |
| 36 // Fuzzing percentage. ex: 10% will spread requests randomly | |
| 37 // between 90%-100% of the calculated time. | |
| 38 0, | |
| 39 | |
| 40 // Maximum amount of time we are willing to delay our request in ms. | |
| 41 10, | |
| 42 | |
| 43 // Time to keep an entry from being discarded even when it | |
| 44 // has no significant state, -1 to never discard. | |
| 45 -1, | |
| 46 | |
| 47 // Don't use initial delay unless the last request was an error. | |
| 48 false, | |
| 49 }; | |
| 50 | |
| 51 // Helper for calculating total expected exponential backoff delay given an | |
| 52 // arbitrary number of failed attempts. See BackoffEntry::CalculateReleaseTime. | |
| 53 int CalculateBackoff(int num_attempts) { | |
| 54 int delay = kBackoffDelayMs; | |
| 55 for (int i = 1; i < num_attempts; ++i) { | |
| 56 delay += kBackoffDelayMs*pow(kBackoffMultiplier, i - 1); | |
| 57 } | |
| 58 DVLOG(1) << "Expected backoff " << delay << " milliseconds."; | |
| 59 return delay; | |
| 60 } | |
| 61 | |
| 62 // Helper methods that are never actually called due to real connections being | |
| 63 // stubbed out. | |
| 64 void ReadContinuation( | |
| 65 scoped_ptr<google::protobuf::MessageLite> message) { | |
| 66 // Do nothing. | |
|
akalin
2013/11/21 04:13:09
if they aren't called, perhaps ADD_FAILURE() to as
Nicolas Zea
2013/11/22 05:39:18
Done.
| |
| 67 } | |
| 68 void WriteContinuation() { | |
|
akalin
2013/11/21 04:13:09
newline before
Nicolas Zea
2013/11/22 05:39:18
Done.
| |
| 69 // Do nothing. | |
| 70 } | |
| 71 | |
| 72 // A connection factory that stubs out network requests and overrides the | |
| 73 // backoff policy. | |
| 74 class TestConnectionFactoryImpl : public ConnectionFactoryImpl { | |
| 75 public: | |
| 76 TestConnectionFactoryImpl(const base::Closure& finished_callback); | |
| 77 virtual ~TestConnectionFactoryImpl(); | |
| 78 | |
| 79 // Overridden stubs. | |
| 80 virtual void ConnectImpl() OVERRIDE; | |
| 81 virtual void InitHandler() OVERRIDE; | |
| 82 virtual scoped_ptr<net::BackoffEntry> CreateBackoffEntry( | |
| 83 const net::BackoffEntry::Policy* const policy) OVERRIDE; | |
| 84 | |
| 85 // Helpers for verifying connection attempts are made. Connection results | |
| 86 // must be consumed. | |
| 87 void SetConnectResult(int connect_result); | |
| 88 void SetMultipleConnectResults(int connect_result, int num_expected_attempts); | |
| 89 | |
| 90 private: | |
| 91 // The result to return on the next connect attempt. | |
| 92 int connect_result_; | |
| 93 // The number of expected connection attempts; | |
| 94 int num_expected_attempts_; | |
| 95 // Whether all expected connection attempts have been fulfilled since an | |
| 96 // expectation was last set. | |
| 97 bool connections_fulfilled_; | |
| 98 // Callback to invoke when all connection attempts have been made. | |
| 99 base::Closure finished_callback_; | |
| 100 }; | |
| 101 | |
| 102 TestConnectionFactoryImpl::TestConnectionFactoryImpl( | |
| 103 const base::Closure& finished_callback) | |
| 104 : ConnectionFactoryImpl(GURL(kMCSEndpoint), NULL, NULL), | |
| 105 connect_result_(net::ERR_UNEXPECTED), | |
| 106 num_expected_attempts_(0), | |
| 107 connections_fulfilled_(true), | |
| 108 finished_callback_(finished_callback) { | |
| 109 } | |
| 110 | |
| 111 TestConnectionFactoryImpl::~TestConnectionFactoryImpl() { | |
| 112 EXPECT_EQ(0, num_expected_attempts_); | |
| 113 } | |
| 114 | |
| 115 void TestConnectionFactoryImpl::ConnectImpl() { | |
| 116 ASSERT_GT(num_expected_attempts_, 0); | |
| 117 | |
| 118 OnConnectDone(connect_result_); | |
| 119 --num_expected_attempts_; | |
| 120 if (num_expected_attempts_ == 0) { | |
| 121 connect_result_ = net::ERR_UNEXPECTED; | |
| 122 connections_fulfilled_ = true; | |
| 123 finished_callback_.Run(); | |
| 124 } | |
| 125 } | |
| 126 | |
| 127 void TestConnectionFactoryImpl::InitHandler() { | |
| 128 EXPECT_NE(connect_result_, net::ERR_UNEXPECTED); | |
| 129 } | |
| 130 | |
| 131 scoped_ptr<net::BackoffEntry> TestConnectionFactoryImpl::CreateBackoffEntry( | |
| 132 const net::BackoffEntry::Policy* const policy) { | |
| 133 return scoped_ptr<net::BackoffEntry>( | |
| 134 new net::BackoffEntry(&kTestBackoffPolicy)); | |
| 135 } | |
| 136 | |
| 137 void TestConnectionFactoryImpl::SetConnectResult(int connect_result) { | |
| 138 DCHECK_NE(connect_result, net::ERR_UNEXPECTED); | |
| 139 ASSERT_EQ(0, num_expected_attempts_); | |
| 140 connections_fulfilled_ = false; | |
| 141 connect_result_ = connect_result; | |
| 142 num_expected_attempts_ = 1; | |
| 143 } | |
| 144 | |
| 145 void TestConnectionFactoryImpl::SetMultipleConnectResults( | |
| 146 int connect_result, | |
| 147 int num_expected_attempts) { | |
| 148 DCHECK_NE(connect_result, net::ERR_UNEXPECTED); | |
| 149 DCHECK_GT(num_expected_attempts, 0); | |
| 150 ASSERT_EQ(0, num_expected_attempts_); | |
| 151 connections_fulfilled_ = false; | |
| 152 connect_result_ = connect_result; | |
| 153 num_expected_attempts_ = num_expected_attempts; | |
| 154 } | |
| 155 | |
| 156 class ConnectionFactoryImplTest : public testing::Test { | |
| 157 public: | |
| 158 ConnectionFactoryImplTest(); | |
| 159 virtual ~ConnectionFactoryImplTest(); | |
| 160 | |
| 161 TestConnectionFactoryImpl* factory() { return &factory_; } | |
| 162 | |
| 163 void WaitForConnections(); | |
| 164 | |
| 165 private: | |
| 166 void ConnectionsComplete(); | |
| 167 | |
| 168 TestConnectionFactoryImpl factory_; | |
| 169 base::MessageLoop message_loop_; | |
| 170 scoped_ptr<base::RunLoop> run_loop_; | |
| 171 }; | |
| 172 | |
| 173 ConnectionFactoryImplTest::ConnectionFactoryImplTest() | |
| 174 : factory_(base::Bind(&ConnectionFactoryImplTest::ConnectionsComplete, | |
| 175 base::Unretained(this))), | |
| 176 run_loop_(new base::RunLoop()) {} | |
| 177 ConnectionFactoryImplTest::~ConnectionFactoryImplTest() {} | |
| 178 | |
| 179 void ConnectionFactoryImplTest::WaitForConnections() { | |
| 180 run_loop_->Run(); | |
| 181 run_loop_.reset(new base::RunLoop()); | |
| 182 } | |
| 183 | |
| 184 void ConnectionFactoryImplTest::ConnectionsComplete() { | |
| 185 if (!run_loop_) | |
| 186 return; | |
| 187 run_loop_->Quit(); | |
| 188 } | |
| 189 | |
| 190 // Verify building a connection handler works. | |
| 191 TEST_F(ConnectionFactoryImplTest, BuildConnectionHandler) { | |
| 192 EXPECT_FALSE(factory()->IsEndpointReachable()); | |
| 193 ConnectionHandler* handler = factory()->BuildConnectionHandler( | |
| 194 base::Bind(&ReadContinuation), | |
| 195 base::Bind(&WriteContinuation)); | |
| 196 ASSERT_TRUE(handler); | |
| 197 EXPECT_FALSE(factory()->IsEndpointReachable()); | |
| 198 } | |
| 199 | |
| 200 // An initial successful connection should not result in backoff. | |
| 201 TEST_F(ConnectionFactoryImplTest, ConnectSuccess) { | |
| 202 factory()->BuildConnectionHandler( | |
| 203 ConnectionHandler::ProtoReceivedCallback(), | |
| 204 ConnectionHandler::ProtoSentCallback()); | |
| 205 factory()->SetConnectResult(net::OK); | |
| 206 factory()->Connect(mcs_proto::LoginRequest()); | |
| 207 EXPECT_TRUE(factory()->NextRetryAttempt().is_null()); | |
| 208 } | |
| 209 | |
| 210 // A connection failure should result in backoff. | |
| 211 TEST_F(ConnectionFactoryImplTest, ConnectFail) { | |
| 212 factory()->BuildConnectionHandler( | |
| 213 ConnectionHandler::ProtoReceivedCallback(), | |
| 214 ConnectionHandler::ProtoSentCallback()); | |
| 215 factory()->SetConnectResult(net::ERR_CONNECTION_FAILED); | |
| 216 factory()->Connect(mcs_proto::LoginRequest()); | |
| 217 EXPECT_FALSE(factory()->NextRetryAttempt().is_null()); | |
| 218 } | |
| 219 | |
| 220 // A connection success after a failure should reset backoff. | |
| 221 TEST_F(ConnectionFactoryImplTest, FailThenSucceed) { | |
| 222 factory()->BuildConnectionHandler( | |
| 223 ConnectionHandler::ProtoReceivedCallback(), | |
| 224 ConnectionHandler::ProtoSentCallback()); | |
| 225 factory()->SetConnectResult(net::ERR_CONNECTION_FAILED); | |
| 226 base::TimeTicks connect_time = base::TimeTicks::Now(); | |
| 227 factory()->Connect(mcs_proto::LoginRequest()); | |
| 228 WaitForConnections(); | |
| 229 base::TimeTicks retry_time = factory()->NextRetryAttempt(); | |
| 230 EXPECT_FALSE(retry_time.is_null()); | |
| 231 EXPECT_GE((retry_time - connect_time).InMilliseconds(), CalculateBackoff(1)); | |
| 232 factory()->SetConnectResult(net::OK); | |
| 233 WaitForConnections(); | |
| 234 EXPECT_TRUE(factory()->NextRetryAttempt().is_null()); | |
| 235 } | |
| 236 | |
| 237 // Multiple connection failures should retry with an exponentially increasing | |
| 238 // backoff, then reset on success. | |
| 239 TEST_F(ConnectionFactoryImplTest, MultipleFailuresThenSucceed) { | |
| 240 factory()->BuildConnectionHandler( | |
| 241 ConnectionHandler::ProtoReceivedCallback(), | |
| 242 ConnectionHandler::ProtoSentCallback()); | |
| 243 | |
| 244 const int kNumAttempts = 5; | |
| 245 factory()->SetMultipleConnectResults(net::ERR_CONNECTION_FAILED, | |
| 246 kNumAttempts); | |
| 247 | |
| 248 base::TimeTicks connect_time = base::TimeTicks::Now(); | |
| 249 factory()->Connect(mcs_proto::LoginRequest()); | |
| 250 WaitForConnections(); | |
| 251 base::TimeTicks retry_time = factory()->NextRetryAttempt(); | |
| 252 EXPECT_FALSE(retry_time.is_null()); | |
| 253 EXPECT_GE((retry_time - connect_time).InMilliseconds(), | |
| 254 CalculateBackoff(kNumAttempts)); | |
| 255 | |
| 256 factory()->SetConnectResult(net::OK); | |
| 257 WaitForConnections(); | |
| 258 EXPECT_TRUE(factory()->NextRetryAttempt().is_null()); | |
| 259 } | |
| 260 | |
| 261 // IP events should reset backoff. | |
| 262 TEST_F(ConnectionFactoryImplTest, FailThenIPEvent) { | |
| 263 factory()->BuildConnectionHandler( | |
| 264 ConnectionHandler::ProtoReceivedCallback(), | |
| 265 ConnectionHandler::ProtoSentCallback()); | |
| 266 factory()->SetConnectResult(net::ERR_CONNECTION_FAILED); | |
| 267 factory()->Connect(mcs_proto::LoginRequest()); | |
| 268 WaitForConnections(); | |
| 269 EXPECT_FALSE(factory()->NextRetryAttempt().is_null()); | |
| 270 | |
| 271 factory()->OnIPAddressChanged(); | |
| 272 EXPECT_TRUE(factory()->NextRetryAttempt().is_null()); | |
| 273 } | |
| 274 | |
| 275 // Connection type events should reset backoff. | |
| 276 TEST_F(ConnectionFactoryImplTest, FailThenConnectionTypeEvent) { | |
| 277 factory()->BuildConnectionHandler( | |
| 278 ConnectionHandler::ProtoReceivedCallback(), | |
| 279 ConnectionHandler::ProtoSentCallback()); | |
| 280 factory()->SetConnectResult(net::ERR_CONNECTION_FAILED); | |
| 281 factory()->Connect(mcs_proto::LoginRequest()); | |
| 282 WaitForConnections(); | |
| 283 EXPECT_FALSE(factory()->NextRetryAttempt().is_null()); | |
| 284 | |
| 285 factory()->OnConnectionTypeChanged( | |
| 286 net::NetworkChangeNotifier::CONNECTION_WIFI); | |
| 287 EXPECT_TRUE(factory()->NextRetryAttempt().is_null()); | |
| 288 } | |
| 289 | |
| 290 } // namespace | |
| 291 } // namespace gcm | |
| OLD | NEW |