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.
5 #include "google_apis/gcm/engine/connection_factory_impl.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/run_loop.h"
11 #include "base/test/simple_test_tick_clock.h"
12 #include "google_apis/gcm/base/mcs_util.h"
13 #include "google_apis/gcm/engine/fake_connection_handler.h"
14 #include "net/base/backoff_entry.h"
15 #include "net/http/http_network_session.h"
16 #include "testing/gtest/include/gtest/gtest.h"
23 const char kMCSEndpoint[] = "http://my.server";
24 const char kMCSEndpoint2[] = "http://my.alt.server";
26 const int kBackoffDelayMs = 1;
27 const int kBackoffMultiplier = 2;
29 // A backoff policy with small enough delays that tests aren't burdened.
30 const net::BackoffEntry::Policy kTestBackoffPolicy = {
31 // Number of initial errors (in sequence) to ignore before applying
32 // exponential back-off rules.
35 // Initial delay for exponential back-off in ms.
38 // Factor by which the waiting time will be multiplied.
41 // Fuzzing percentage. ex: 10% will spread requests randomly
42 // between 90%-100% of the calculated time.
45 // Maximum amount of time we are willing to delay our request in ms.
48 // Time to keep an entry from being discarded even when it
49 // has no significant state, -1 to never discard.
52 // Don't use initial delay unless the last request was an error.
56 std::vector<GURL> BuildEndpoints() {
57 std::vector<GURL> endpoints;
58 endpoints.push_back(GURL(kMCSEndpoint));
59 endpoints.push_back(GURL(kMCSEndpoint2));
63 // Helper for calculating total expected exponential backoff delay given an
64 // arbitrary number of failed attempts. See BackoffEntry::CalculateReleaseTime.
65 double CalculateBackoff(int num_attempts) {
66 double delay = kBackoffDelayMs;
67 for (int i = 1; i < num_attempts; ++i) {
68 delay += kBackoffDelayMs * pow(static_cast<double>(kBackoffMultiplier),
71 DVLOG(1) << "Expected backoff " << delay << " milliseconds.";
75 void ReadContinuation(
76 scoped_ptr<google::protobuf::MessageLite> message) {
79 void WriteContinuation() {
82 class TestBackoffEntry : public net::BackoffEntry {
84 explicit TestBackoffEntry(base::SimpleTestTickClock* tick_clock);
85 virtual ~TestBackoffEntry();
87 virtual base::TimeTicks ImplGetTimeNow() const OVERRIDE;
90 base::SimpleTestTickClock* tick_clock_;
93 TestBackoffEntry::TestBackoffEntry(base::SimpleTestTickClock* tick_clock)
94 : BackoffEntry(&kTestBackoffPolicy),
95 tick_clock_(tick_clock) {
98 TestBackoffEntry::~TestBackoffEntry() {}
100 base::TimeTicks TestBackoffEntry::ImplGetTimeNow() const {
101 return tick_clock_->NowTicks();
104 // A connection factory that stubs out network requests and overrides the
106 class TestConnectionFactoryImpl : public ConnectionFactoryImpl {
108 TestConnectionFactoryImpl(const base::Closure& finished_callback);
109 virtual ~TestConnectionFactoryImpl();
111 void InitializeFactory();
114 virtual void ConnectImpl() OVERRIDE;
115 virtual void InitHandler() OVERRIDE;
116 virtual scoped_ptr<net::BackoffEntry> CreateBackoffEntry(
117 const net::BackoffEntry::Policy* const policy) OVERRIDE;
118 virtual scoped_ptr<ConnectionHandler> CreateConnectionHandler(
119 base::TimeDelta read_timeout,
120 const ConnectionHandler::ProtoReceivedCallback& read_callback,
121 const ConnectionHandler::ProtoSentCallback& write_callback,
122 const ConnectionHandler::ConnectionChangedCallback& connection_callback)
124 virtual base::TimeTicks NowTicks() OVERRIDE;
126 // Helpers for verifying connection attempts are made. Connection results
128 void SetConnectResult(int connect_result);
129 void SetMultipleConnectResults(int connect_result, int num_expected_attempts);
131 base::SimpleTestTickClock* tick_clock() { return &tick_clock_; }
134 // Clock for controlling delay.
135 base::SimpleTestTickClock tick_clock_;
136 // The result to return on the next connect attempt.
138 // The number of expected connection attempts;
139 int num_expected_attempts_;
140 // Whether all expected connection attempts have been fulfilled since an
141 // expectation was last set.
142 bool connections_fulfilled_;
143 // Callback to invoke when all connection attempts have been made.
144 base::Closure finished_callback_;
145 // The current fake connection handler..
146 FakeConnectionHandler* fake_handler_;
149 TestConnectionFactoryImpl::TestConnectionFactoryImpl(
150 const base::Closure& finished_callback)
151 : ConnectionFactoryImpl(BuildEndpoints(),
152 net::BackoffEntry::Policy(),
155 connect_result_(net::ERR_UNEXPECTED),
156 num_expected_attempts_(0),
157 connections_fulfilled_(true),
158 finished_callback_(finished_callback),
159 fake_handler_(NULL) {
160 // Set a non-null time.
161 tick_clock_.Advance(base::TimeDelta::FromMilliseconds(1));
164 TestConnectionFactoryImpl::~TestConnectionFactoryImpl() {
165 EXPECT_EQ(0, num_expected_attempts_);
168 void TestConnectionFactoryImpl::ConnectImpl() {
169 ASSERT_GT(num_expected_attempts_, 0);
170 scoped_ptr<mcs_proto::LoginRequest> request(BuildLoginRequest(0, 0, ""));
171 GetConnectionHandler()->Init(*request, NULL);
172 OnConnectDone(connect_result_);
173 if (!NextRetryAttempt().is_null()) {
174 // Advance the time to the next retry time.
175 base::TimeDelta time_till_retry =
176 NextRetryAttempt() - tick_clock_.NowTicks();
177 tick_clock_.Advance(time_till_retry);
179 --num_expected_attempts_;
180 if (num_expected_attempts_ == 0) {
181 connect_result_ = net::ERR_UNEXPECTED;
182 connections_fulfilled_ = true;
183 finished_callback_.Run();
187 void TestConnectionFactoryImpl::InitHandler() {
188 EXPECT_NE(connect_result_, net::ERR_UNEXPECTED);
189 ConnectionHandlerCallback(net::OK);
192 scoped_ptr<net::BackoffEntry> TestConnectionFactoryImpl::CreateBackoffEntry(
193 const net::BackoffEntry::Policy* const policy) {
194 return scoped_ptr<net::BackoffEntry>(new TestBackoffEntry(&tick_clock_));
197 scoped_ptr<ConnectionHandler>
198 TestConnectionFactoryImpl::CreateConnectionHandler(
199 base::TimeDelta read_timeout,
200 const ConnectionHandler::ProtoReceivedCallback& read_callback,
201 const ConnectionHandler::ProtoSentCallback& write_callback,
202 const ConnectionHandler::ConnectionChangedCallback& connection_callback) {
203 fake_handler_ = new FakeConnectionHandler(
204 base::Bind(&ReadContinuation),
205 base::Bind(&WriteContinuation));
206 return make_scoped_ptr<ConnectionHandler>(fake_handler_);
209 base::TimeTicks TestConnectionFactoryImpl::NowTicks() {
210 return tick_clock_.NowTicks();
213 void TestConnectionFactoryImpl::SetConnectResult(int connect_result) {
214 DCHECK_NE(connect_result, net::ERR_UNEXPECTED);
215 ASSERT_EQ(0, num_expected_attempts_);
216 connections_fulfilled_ = false;
217 connect_result_ = connect_result;
218 num_expected_attempts_ = 1;
219 fake_handler_->ExpectOutgoingMessage(
220 MCSMessage(kLoginRequestTag,
221 BuildLoginRequest(0, 0, "").PassAs<
222 const google::protobuf::MessageLite>()));
225 void TestConnectionFactoryImpl::SetMultipleConnectResults(
227 int num_expected_attempts) {
228 DCHECK_NE(connect_result, net::ERR_UNEXPECTED);
229 DCHECK_GT(num_expected_attempts, 0);
230 ASSERT_EQ(0, num_expected_attempts_);
231 connections_fulfilled_ = false;
232 connect_result_ = connect_result;
233 num_expected_attempts_ = num_expected_attempts;
234 for (int i = 0 ; i < num_expected_attempts; ++i) {
235 fake_handler_->ExpectOutgoingMessage(
236 MCSMessage(kLoginRequestTag,
237 BuildLoginRequest(0, 0, "").PassAs<
238 const google::protobuf::MessageLite>()));
242 class ConnectionFactoryImplTest : public testing::Test {
244 ConnectionFactoryImplTest();
245 virtual ~ConnectionFactoryImplTest();
247 TestConnectionFactoryImpl* factory() { return &factory_; }
249 void WaitForConnections();
252 void ConnectionsComplete();
254 TestConnectionFactoryImpl factory_;
255 base::MessageLoop message_loop_;
256 scoped_ptr<base::RunLoop> run_loop_;
259 ConnectionFactoryImplTest::ConnectionFactoryImplTest()
260 : factory_(base::Bind(&ConnectionFactoryImplTest::ConnectionsComplete,
261 base::Unretained(this))),
262 run_loop_(new base::RunLoop()) {
263 factory()->Initialize(
264 ConnectionFactory::BuildLoginRequestCallback(),
265 ConnectionHandler::ProtoReceivedCallback(),
266 ConnectionHandler::ProtoSentCallback());
268 ConnectionFactoryImplTest::~ConnectionFactoryImplTest() {}
270 void ConnectionFactoryImplTest::WaitForConnections() {
272 run_loop_.reset(new base::RunLoop());
275 void ConnectionFactoryImplTest::ConnectionsComplete() {
281 // Verify building a connection handler works.
282 TEST_F(ConnectionFactoryImplTest, Initialize) {
283 ConnectionHandler* handler = factory()->GetConnectionHandler();
284 ASSERT_TRUE(handler);
285 EXPECT_FALSE(factory()->IsEndpointReachable());
288 // An initial successful connection should not result in backoff.
289 TEST_F(ConnectionFactoryImplTest, ConnectSuccess) {
290 factory()->SetConnectResult(net::OK);
291 factory()->Connect();
292 EXPECT_TRUE(factory()->NextRetryAttempt().is_null());
293 EXPECT_EQ(factory()->GetCurrentEndpoint(), BuildEndpoints()[0]);
294 EXPECT_TRUE(factory()->IsEndpointReachable());
297 // A connection failure should result in backoff, and attempting the fallback
299 TEST_F(ConnectionFactoryImplTest, ConnectFail) {
300 factory()->SetConnectResult(net::ERR_CONNECTION_FAILED);
301 factory()->Connect();
302 EXPECT_FALSE(factory()->NextRetryAttempt().is_null());
303 EXPECT_EQ(factory()->GetCurrentEndpoint(), BuildEndpoints()[1]);
304 EXPECT_FALSE(factory()->IsEndpointReachable());
307 // A connection success after a failure should reset backoff.
308 TEST_F(ConnectionFactoryImplTest, FailThenSucceed) {
309 factory()->SetConnectResult(net::ERR_CONNECTION_FAILED);
310 base::TimeTicks connect_time = factory()->tick_clock()->NowTicks();
311 factory()->Connect();
312 WaitForConnections();
313 EXPECT_FALSE(factory()->IsEndpointReachable());
314 base::TimeTicks retry_time = factory()->NextRetryAttempt();
315 EXPECT_FALSE(retry_time.is_null());
316 EXPECT_GE((retry_time - connect_time).InMilliseconds(), CalculateBackoff(1));
317 factory()->SetConnectResult(net::OK);
318 WaitForConnections();
319 EXPECT_TRUE(factory()->NextRetryAttempt().is_null());
320 EXPECT_TRUE(factory()->IsEndpointReachable());
323 // Multiple connection failures should retry with an exponentially increasing
324 // backoff, then reset on success.
325 TEST_F(ConnectionFactoryImplTest, MultipleFailuresThenSucceed) {
327 const int kNumAttempts = 5;
328 factory()->SetMultipleConnectResults(net::ERR_CONNECTION_FAILED,
331 base::TimeTicks connect_time = factory()->tick_clock()->NowTicks();
332 factory()->Connect();
333 WaitForConnections();
334 EXPECT_FALSE(factory()->IsEndpointReachable());
335 base::TimeTicks retry_time = factory()->NextRetryAttempt();
336 EXPECT_FALSE(retry_time.is_null());
337 EXPECT_GE((retry_time - connect_time).InMilliseconds(),
338 CalculateBackoff(kNumAttempts));
340 factory()->SetConnectResult(net::OK);
341 WaitForConnections();
342 EXPECT_TRUE(factory()->NextRetryAttempt().is_null());
343 EXPECT_TRUE(factory()->IsEndpointReachable());
346 // IP events should trigger canary connections.
347 TEST_F(ConnectionFactoryImplTest, FailThenIPEvent) {
348 factory()->SetConnectResult(net::ERR_CONNECTION_FAILED);
349 factory()->Connect();
350 WaitForConnections();
351 base::TimeTicks initial_backoff = factory()->NextRetryAttempt();
352 EXPECT_FALSE(initial_backoff.is_null());
354 factory()->SetConnectResult(net::ERR_FAILED);
355 factory()->OnIPAddressChanged();
356 WaitForConnections();
358 // Backoff should increase.
359 base::TimeTicks next_backoff = factory()->NextRetryAttempt();
360 EXPECT_GT(next_backoff, initial_backoff);
361 EXPECT_FALSE(factory()->IsEndpointReachable());
364 // Connection type events should trigger canary connections.
365 TEST_F(ConnectionFactoryImplTest, FailThenConnectionTypeEvent) {
366 factory()->SetConnectResult(net::ERR_CONNECTION_FAILED);
367 factory()->Connect();
368 WaitForConnections();
369 base::TimeTicks initial_backoff = factory()->NextRetryAttempt();
370 EXPECT_FALSE(initial_backoff.is_null());
372 factory()->SetConnectResult(net::ERR_FAILED);
373 factory()->OnConnectionTypeChanged(
374 net::NetworkChangeNotifier::CONNECTION_WIFI);
375 WaitForConnections();
377 // Backoff should increase.
378 base::TimeTicks next_backoff = factory()->NextRetryAttempt();
379 EXPECT_GT(next_backoff, initial_backoff);
380 EXPECT_FALSE(factory()->IsEndpointReachable());
383 // Verify that we reconnect even if a canary succeeded then disconnected while
384 // a backoff was pending.
385 TEST_F(ConnectionFactoryImplTest, CanarySucceedsThenDisconnects) {
386 factory()->SetConnectResult(net::ERR_CONNECTION_FAILED);
387 factory()->Connect();
388 WaitForConnections();
389 base::TimeTicks initial_backoff = factory()->NextRetryAttempt();
390 EXPECT_FALSE(initial_backoff.is_null());
392 factory()->SetConnectResult(net::OK);
393 factory()->OnConnectionTypeChanged(
394 net::NetworkChangeNotifier::CONNECTION_WIFI);
395 WaitForConnections();
396 EXPECT_TRUE(factory()->IsEndpointReachable());
398 factory()->SetConnectResult(net::OK);
399 factory()->SignalConnectionReset(ConnectionFactory::SOCKET_FAILURE);
400 EXPECT_FALSE(factory()->IsEndpointReachable());
401 WaitForConnections();
402 EXPECT_TRUE(factory()->IsEndpointReachable());
405 // Fail after successful connection via signal reset.
406 TEST_F(ConnectionFactoryImplTest, FailViaSignalReset) {
407 factory()->SetConnectResult(net::OK);
408 factory()->Connect();
409 EXPECT_TRUE(factory()->NextRetryAttempt().is_null());
411 factory()->SignalConnectionReset(ConnectionFactory::SOCKET_FAILURE);
412 EXPECT_FALSE(factory()->NextRetryAttempt().is_null());
413 EXPECT_FALSE(factory()->IsEndpointReachable());
416 TEST_F(ConnectionFactoryImplTest, IgnoreResetWhileConnecting) {
417 factory()->SetConnectResult(net::OK);
418 factory()->Connect();
419 EXPECT_TRUE(factory()->NextRetryAttempt().is_null());
421 factory()->SignalConnectionReset(ConnectionFactory::SOCKET_FAILURE);
422 base::TimeTicks retry_time = factory()->NextRetryAttempt();
423 EXPECT_FALSE(retry_time.is_null());
424 EXPECT_FALSE(factory()->IsEndpointReachable());
426 const int kNumAttempts = 5;
427 for (int i = 0; i < kNumAttempts; ++i)
428 factory()->SignalConnectionReset(ConnectionFactory::SOCKET_FAILURE);
429 EXPECT_EQ(retry_time, factory()->NextRetryAttempt());
430 EXPECT_FALSE(factory()->IsEndpointReachable());
433 // Go into backoff due to connection failure. On successful connection, receive
434 // a signal reset. The original backoff should be restored and extended, rather
435 // than a new backoff starting from scratch.
436 TEST_F(ConnectionFactoryImplTest, SignalResetRestoresBackoff) {
437 factory()->SetConnectResult(net::ERR_CONNECTION_FAILED);
438 base::TimeTicks connect_time = factory()->tick_clock()->NowTicks();
439 factory()->Connect();
440 WaitForConnections();
441 base::TimeTicks retry_time = factory()->NextRetryAttempt();
442 EXPECT_FALSE(retry_time.is_null());
444 factory()->SetConnectResult(net::OK);
445 connect_time = factory()->tick_clock()->NowTicks();
446 WaitForConnections();
447 EXPECT_TRUE(factory()->NextRetryAttempt().is_null());
449 factory()->SignalConnectionReset(ConnectionFactory::SOCKET_FAILURE);
450 EXPECT_FALSE(factory()->IsEndpointReachable());
451 EXPECT_NE(retry_time, factory()->NextRetryAttempt());
452 retry_time = factory()->NextRetryAttempt();
453 EXPECT_FALSE(retry_time.is_null());
454 EXPECT_GE((retry_time - connect_time).InMilliseconds(),
455 CalculateBackoff(2));
457 factory()->SetConnectResult(net::OK);
458 connect_time = factory()->tick_clock()->NowTicks();
459 factory()->tick_clock()->Advance(
460 factory()->NextRetryAttempt() - connect_time);
461 WaitForConnections();
462 EXPECT_TRUE(factory()->NextRetryAttempt().is_null());
463 EXPECT_TRUE(factory()->IsEndpointReachable());
465 factory()->SignalConnectionReset(ConnectionFactory::SOCKET_FAILURE);
466 EXPECT_NE(retry_time, factory()->NextRetryAttempt());
467 retry_time = factory()->NextRetryAttempt();
468 EXPECT_FALSE(retry_time.is_null());
469 EXPECT_GE((retry_time - connect_time).InMilliseconds(),
470 CalculateBackoff(3));
471 EXPECT_FALSE(factory()->IsEndpointReachable());