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 |