OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 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 "components/password_manager/core/browser/affiliation_fetch_throttler.h " | |
6 | |
7 #include <cmath> | |
8 | |
9 #include "base/callback.h" | |
10 #include "base/macros.h" | |
11 #include "base/memory/ref_counted.h" | |
12 #include "base/memory/scoped_ptr.h" | |
13 #include "base/message_loop/message_loop.h" | |
14 #include "base/numerics/safe_math.h" | |
15 #include "base/run_loop.h" | |
16 #include "base/test/test_mock_time_task_runner.h" | |
17 #include "base/thread_task_runner_handle.h" | |
18 #include "base/time/tick_clock.h" | |
19 #include "base/time/time.h" | |
20 #include "components/password_manager/core/browser/affiliation_fetch_throttler_d elegate.h" | |
21 #include "net/base/network_change_notifier.h" | |
22 #include "testing/gtest/include/gtest/gtest.h" | |
23 | |
24 namespace password_manager { | |
25 namespace { | |
26 | |
27 class MockAffiliationFetchThrottlerDelegate | |
28 : public AffiliationFetchThrottlerDelegate { | |
29 public: | |
30 MockAffiliationFetchThrottlerDelegate(scoped_ptr<base::TickClock> tick_clock) | |
mmenke
2015/01/30 19:50:41
nit: explicit
engedy
2015/01/30 20:23:13
Done.
| |
31 : tick_clock_(tick_clock.Pass()), | |
32 emulated_return_value_(true), | |
33 release_count_(0u) {} | |
34 ~MockAffiliationFetchThrottlerDelegate() { EXPECT_EQ(0u, release_count_); } | |
35 | |
36 void set_emulated_return_value(bool value) { emulated_return_value_ = value; } | |
37 void reset_release_count() { release_count_ = 0u; } | |
mmenke
2015/01/30 19:50:41
optional: Think this variable name is a little un
engedy
2015/01/30 20:23:13
Done.
| |
38 size_t release_count() const { return release_count_; } | |
39 base::TimeTicks last_release_time() const { return last_release_time_; } | |
40 | |
41 // AffiliationFetchThrottlerDelegate: | |
42 bool OnCanSendNetworkRequest() override { | |
43 ++release_count_; | |
44 last_release_time_ = tick_clock_->NowTicks(); | |
45 return emulated_return_value_; | |
46 } | |
47 | |
48 private: | |
49 scoped_ptr<base::TickClock> tick_clock_; | |
50 bool emulated_return_value_; | |
51 size_t release_count_; | |
52 base::TimeTicks last_release_time_; | |
53 | |
54 DISALLOW_COPY_AND_ASSIGN(MockAffiliationFetchThrottlerDelegate); | |
55 }; | |
56 | |
57 } // namespace | |
58 | |
59 class AffiliationFetchThrottlerTest : public testing::Test { | |
60 public: | |
61 AffiliationFetchThrottlerTest() | |
62 : network_change_notifier_(net::NetworkChangeNotifier::CreateMock()), | |
63 task_runner_(new base::TestMockTimeTaskRunner), | |
64 mock_delegate_(task_runner_->GetMockTickClock()) {} | |
65 ~AffiliationFetchThrottlerTest() override {} | |
66 | |
67 scoped_ptr<AffiliationFetchThrottler> CreateThrottler() { | |
68 return make_scoped_ptr(new AffiliationFetchThrottler( | |
69 &mock_delegate_, task_runner_, task_runner_->GetMockTickClock())); | |
70 } | |
71 | |
72 void SimulateHasNetworkConnectivity(bool has_connectivity) { | |
73 net::NetworkChangeNotifier::NotifyObserversOfConnectionTypeChangeForTests( | |
74 has_connectivity ? net::NetworkChangeNotifier::CONNECTION_UNKNOWN | |
75 : net::NetworkChangeNotifier::CONNECTION_NONE); | |
76 base::RunLoop().RunUntilIdle(); | |
77 } | |
78 | |
79 // Runs the task runner until no tasks remain, and asserts that by this time, | |
80 // OnCanSendNetworkRequest() will have been called exactly once, with a delay | |
81 // between |min_delay_ms| and |max_delay_ms|, modulo 0.5 ms to allow for | |
82 // floating point errors. When OnCanSendNetworkRequest() is called, the mock | |
83 // will return |emulated_return_value|. This value normally indicates whether | |
84 // or not a request was actually issued in response to the call. | |
85 void AssertReleaseInBetween(bool emulated_return_value, | |
86 double min_delay_ms, | |
87 double max_delay_ms) { | |
88 ASSERT_EQ(0u, mock_delegate_.release_count()); | |
89 base::TimeTicks ticks_at_start = task_runner_->GetCurrentMockTime(); | |
90 mock_delegate_.set_emulated_return_value(emulated_return_value); | |
91 task_runner_->FastForwardUntilNoTasksRemain(); | |
92 ASSERT_EQ(1u, mock_delegate_.release_count()); | |
93 base::TimeDelta delay = mock_delegate_.last_release_time() - ticks_at_start; | |
94 EXPECT_LE(min_delay_ms - 1, delay.InMillisecondsF()); | |
95 EXPECT_GE(max_delay_ms + 1, delay.InMillisecondsF()); | |
96 mock_delegate_.reset_release_count(); | |
97 } | |
98 | |
99 // Runs the task runner for |secs| and asserts that OnCanSendNetworkRequest() | |
100 // will not have been called by the end of this period. | |
101 void AssertNoReleaseForSecs(int64_t secs) { | |
102 task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(secs)); | |
103 ASSERT_EQ(0u, mock_delegate_.release_count()); | |
104 } | |
105 | |
106 // Runs the task runner until no tasks remain, and asserts that | |
107 // OnCanSendNetworkRequest() will not have been called. | |
108 void AssertNoReleaseUntilNoTasksRemain() { | |
109 task_runner_->FastForwardUntilNoTasksRemain(); | |
110 ASSERT_EQ(0u, mock_delegate_.release_count()); | |
111 } | |
112 | |
113 size_t GetPendingTaskCount() const { | |
114 return task_runner_->GetPendingTaskCount(); | |
115 } | |
116 | |
117 private: | |
118 // Needed because NetworkChangeNotifier uses ObserverList, which notifies | |
119 // observers on the MessageLoop that belongs to the thread from which they | |
120 // have registered. | |
121 base::MessageLoop message_loop_; | |
122 scoped_ptr<net::NetworkChangeNotifier> network_change_notifier_; | |
123 scoped_refptr<base::TestMockTimeTaskRunner> task_runner_; | |
124 MockAffiliationFetchThrottlerDelegate mock_delegate_; | |
125 | |
126 DISALLOW_COPY_AND_ASSIGN(AffiliationFetchThrottlerTest); | |
127 }; | |
128 | |
129 TEST_F(AffiliationFetchThrottlerTest, SuccessfulRequests) { | |
130 scoped_ptr<AffiliationFetchThrottler> throttler(CreateThrottler()); | |
131 | |
132 throttler->SignalNetworkRequestNeeded(); | |
133 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0)); | |
134 | |
135 // Signal while request is in flight should be ignored. | |
136 throttler->SignalNetworkRequestNeeded(); | |
137 AssertNoReleaseUntilNoTasksRemain(); | |
138 throttler->InformOfNetworkRequestComplete(true); | |
139 AssertNoReleaseUntilNoTasksRemain(); | |
140 | |
141 // Duplicate the second signal 3 times: still only 1 callback should arrive. | |
142 throttler->SignalNetworkRequestNeeded(); | |
143 throttler->SignalNetworkRequestNeeded(); | |
144 throttler->SignalNetworkRequestNeeded(); | |
145 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0)); | |
146 } | |
147 | |
148 TEST_F(AffiliationFetchThrottlerTest, FailedRequests) { | |
149 scoped_ptr<AffiliationFetchThrottler> throttler(CreateThrottler()); | |
150 | |
151 throttler->SignalNetworkRequestNeeded(); | |
152 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0)); | |
153 throttler->InformOfNetworkRequestComplete(false); | |
154 | |
155 // The request after the first failure should be delayed by |initial_delay_ms| | |
156 // spread out over Uniform(1 - |jitter_factor|, 1). | |
157 throttler->SignalNetworkRequestNeeded(); | |
158 const auto& kPolicy = AffiliationFetchThrottler::kBackoffPolicy; | |
159 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween( | |
160 true, kPolicy.initial_delay_ms * (1 - kPolicy.jitter_factor), | |
161 kPolicy.initial_delay_ms)); | |
162 throttler->InformOfNetworkRequestComplete(true); | |
163 | |
164 // After a successful request, the next one should be released immediately. | |
165 throttler->SignalNetworkRequestNeeded(); | |
166 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0)); | |
167 throttler->InformOfNetworkRequestComplete(false); | |
168 | |
169 // In general, the request after the n-th failure should be delayed by | |
170 // |multiply_factor| ^ (n-1) * |initial_delay_ms|, | |
171 // spread out over Uniform(1 - |jitter_factor|, 1), up until | |
172 // |maximum_backoff_ms| | |
173 // is reached. | |
174 for (size_t num_failures = 1; num_failures < 100; ++num_failures) { | |
175 throttler->SignalNetworkRequestNeeded(); | |
176 double max_delay_ms = kPolicy.initial_delay_ms * | |
177 pow(kPolicy.multiply_factor, num_failures - 1); | |
178 double min_delay_ms = max_delay_ms * (1 - kPolicy.jitter_factor); | |
179 if (max_delay_ms > kPolicy.maximum_backoff_ms) | |
180 max_delay_ms = kPolicy.maximum_backoff_ms; | |
181 if (min_delay_ms > kPolicy.maximum_backoff_ms) | |
182 min_delay_ms = kPolicy.maximum_backoff_ms; | |
183 ASSERT_NO_FATAL_FAILURE( | |
184 AssertReleaseInBetween(true, min_delay_ms, max_delay_ms)); | |
185 throttler->InformOfNetworkRequestComplete(false); | |
186 } | |
187 } | |
188 | |
189 TEST_F(AffiliationFetchThrottlerTest, OnCanSendNetworkRequestReturnsFalse) { | |
190 scoped_ptr<AffiliationFetchThrottler> throttler(CreateThrottler()); | |
191 | |
192 // A need for a network request is signaled, but as OnCanSendNetworkRequest() | |
193 // is called, the implementation returns false to indicate that the request | |
194 // will not be needed after all. InformOfNetworkRequestComplete() must not be | |
195 // called in this case. | |
196 throttler->SignalNetworkRequestNeeded(); | |
197 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(false, 0, 0)); | |
198 | |
199 // A subsequent signaling, however, should result in OnCanSendNetworkRequest() | |
200 // being called immediately. | |
201 throttler->SignalNetworkRequestNeeded(); | |
202 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0)); | |
203 } | |
204 | |
205 TEST_F(AffiliationFetchThrottlerTest, GracePeriodAfterConnectivityIsRestored) { | |
206 scoped_ptr<AffiliationFetchThrottler> throttler(CreateThrottler()); | |
207 SimulateHasNetworkConnectivity(false); | |
208 | |
209 // After connectivity is restored, the first request should be delayed by the | |
210 // grace period, spread out over Uniform(1 - |jitter_factor|, 1). | |
211 throttler->SignalNetworkRequestNeeded(); | |
212 AssertNoReleaseUntilNoTasksRemain(); | |
213 | |
214 SimulateHasNetworkConnectivity(true); | |
215 const auto& kPolicy = AffiliationFetchThrottler::kBackoffPolicy; | |
216 const int64_t& kGraceMs = | |
217 AffiliationFetchThrottler::kGracePeriodAfterReconnectMs; | |
218 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween( | |
219 true, kGraceMs * (1 - kPolicy.jitter_factor), kGraceMs)); | |
220 throttler->InformOfNetworkRequestComplete(true); | |
221 | |
222 // The next request should not be delayed. | |
223 throttler->SignalNetworkRequestNeeded(); | |
224 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0)); | |
225 } | |
226 | |
227 // Same as GracePeriodAfterConnectivityIsRestored, but the network comes back | |
228 // just before SignalNetworkRequestNeeded() is called. | |
229 TEST_F(AffiliationFetchThrottlerTest, GracePeriodAfterConnectivityIsRestored2) { | |
230 scoped_ptr<AffiliationFetchThrottler> throttler(CreateThrottler()); | |
231 SimulateHasNetworkConnectivity(false); | |
232 | |
233 SimulateHasNetworkConnectivity(true); | |
234 throttler->SignalNetworkRequestNeeded(); | |
235 const auto& kPolicy = AffiliationFetchThrottler::kBackoffPolicy; | |
236 const int64_t& kGraceMs = | |
237 AffiliationFetchThrottler::kGracePeriodAfterReconnectMs; | |
238 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween( | |
239 true, kGraceMs * (1 - kPolicy.jitter_factor), kGraceMs)); | |
240 throttler->InformOfNetworkRequestComplete(true); | |
241 | |
242 throttler->SignalNetworkRequestNeeded(); | |
243 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0)); | |
244 } | |
245 | |
246 TEST_F(AffiliationFetchThrottlerTest, ConnectivityLostDuringBackoff) { | |
247 scoped_ptr<AffiliationFetchThrottler> throttler(CreateThrottler()); | |
248 | |
249 throttler->SignalNetworkRequestNeeded(); | |
250 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0)); | |
251 throttler->InformOfNetworkRequestComplete(false); | |
252 | |
253 throttler->SignalNetworkRequestNeeded(); | |
254 SimulateHasNetworkConnectivity(false); | |
255 | |
256 // Let the exponential backoff delay expire, and verify nothing happens. | |
257 AssertNoReleaseUntilNoTasksRemain(); | |
258 | |
259 // Verify that the request is, however, sent after the normal grace period | |
260 // once connectivity is restored. | |
261 SimulateHasNetworkConnectivity(true); | |
262 const auto& kPolicy = AffiliationFetchThrottler::kBackoffPolicy; | |
263 const int64_t& kGraceMs = | |
264 AffiliationFetchThrottler::kGracePeriodAfterReconnectMs; | |
265 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween( | |
266 true, kGraceMs * (1 - kPolicy.jitter_factor), kGraceMs)); | |
267 throttler->InformOfNetworkRequestComplete(true); | |
268 } | |
269 | |
270 TEST_F(AffiliationFetchThrottlerTest, | |
271 ConnectivityLostAndRestoredDuringBackoff) { | |
272 scoped_ptr<AffiliationFetchThrottler> throttler(CreateThrottler()); | |
273 | |
274 throttler->SignalNetworkRequestNeeded(); | |
275 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0)); | |
276 throttler->InformOfNetworkRequestComplete(false); | |
277 | |
278 throttler->SignalNetworkRequestNeeded(); | |
279 const auto& kPolicy = AffiliationFetchThrottler::kBackoffPolicy; | |
280 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween( | |
281 true, kPolicy.initial_delay_ms * (1 - kPolicy.jitter_factor), | |
282 kPolicy.initial_delay_ms)); | |
283 throttler->InformOfNetworkRequestComplete(false); | |
284 | |
285 SimulateHasNetworkConnectivity(false); | |
286 SimulateHasNetworkConnectivity(true); | |
287 | |
288 // This test expects that the exponential backoff interval after the 2nd error | |
289 // is larger than the normal grace period after connectivity is restored. | |
290 const int64_t& kGraceMs = | |
291 AffiliationFetchThrottler::kGracePeriodAfterReconnectMs; | |
292 EXPECT_PRED_FORMAT2(testing::DoubleLE, kGraceMs, | |
293 kPolicy.initial_delay_ms * kPolicy.multiply_factor); | |
294 | |
295 // The release should come after the longest of the two intervals expire. | |
296 throttler->SignalNetworkRequestNeeded(); | |
297 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween( | |
298 true, kPolicy.initial_delay_ms * kPolicy.multiply_factor * | |
299 (1 - kPolicy.jitter_factor), | |
300 kPolicy.initial_delay_ms * kPolicy.multiply_factor)); | |
301 throttler->InformOfNetworkRequestComplete(false); | |
302 } | |
303 | |
304 TEST_F(AffiliationFetchThrottlerTest, FlakyConnectivity) { | |
305 scoped_ptr<AffiliationFetchThrottler> throttler(CreateThrottler()); | |
306 | |
307 throttler->SignalNetworkRequestNeeded(); | |
308 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0)); | |
309 throttler->InformOfNetworkRequestComplete(false); | |
310 | |
311 // Run for a total of 5 grace periods and simulate connectivity being lost and | |
312 // restored every second. This verifies that a flaky connection will not flood | |
313 // the task queue with lots of of tasks and also that release will not happen | |
314 // while the connection is flaky even once the first grace period has expired. | |
315 throttler->SignalNetworkRequestNeeded(); | |
316 const auto& kPolicy = AffiliationFetchThrottler::kBackoffPolicy; | |
317 const int64_t& kGraceMs = | |
318 AffiliationFetchThrottler::kGracePeriodAfterReconnectMs; | |
319 int64_t five_grace_periods_secs = | |
320 kGraceMs * 5 / base::Time::kMillisecondsPerSecond; | |
321 for (int64_t t = 0; t < five_grace_periods_secs; ++t) { | |
322 SimulateHasNetworkConnectivity(false); | |
323 AssertNoReleaseForSecs(1); | |
324 SimulateHasNetworkConnectivity(true); | |
325 EXPECT_EQ(1u, GetPendingTaskCount()); | |
326 } | |
327 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween( | |
328 true, kGraceMs * (1 - kPolicy.jitter_factor), kGraceMs)); | |
329 } | |
330 | |
331 TEST_F(AffiliationFetchThrottlerTest, ConnectivityLostDuringRequest) { | |
332 scoped_ptr<AffiliationFetchThrottler> throttler(CreateThrottler()); | |
333 | |
334 throttler->SignalNetworkRequestNeeded(); | |
335 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0)); | |
336 | |
337 SimulateHasNetworkConnectivity(false); | |
338 AssertNoReleaseUntilNoTasksRemain(); | |
339 throttler->InformOfNetworkRequestComplete(false); | |
340 AssertNoReleaseUntilNoTasksRemain(); | |
341 throttler->SignalNetworkRequestNeeded(); | |
342 AssertNoReleaseUntilNoTasksRemain(); | |
343 | |
344 SimulateHasNetworkConnectivity(true); | |
345 | |
346 // Verify that the next request is released after the normal grace period. | |
347 const auto& kPolicy = AffiliationFetchThrottler::kBackoffPolicy; | |
348 const int64_t& kGraceMs = | |
349 AffiliationFetchThrottler::kGracePeriodAfterReconnectMs; | |
350 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween( | |
351 true, kGraceMs * (1 - kPolicy.jitter_factor), kGraceMs)); | |
352 throttler->InformOfNetworkRequestComplete(true); | |
353 } | |
354 | |
355 TEST_F(AffiliationFetchThrottlerTest, | |
356 ConnectivityLostAndRestoredDuringRequest) { | |
357 scoped_ptr<AffiliationFetchThrottler> throttler(CreateThrottler()); | |
358 | |
359 throttler->SignalNetworkRequestNeeded(); | |
360 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0)); | |
361 | |
362 SimulateHasNetworkConnectivity(false); | |
363 AssertNoReleaseUntilNoTasksRemain(); | |
364 SimulateHasNetworkConnectivity(true); | |
365 AssertNoReleaseUntilNoTasksRemain(); | |
366 throttler->InformOfNetworkRequestComplete(true); | |
367 | |
368 // Even though the previous request succeeded, the next request should still | |
369 // be held back for the normal grace period after connection is restored. | |
370 throttler->SignalNetworkRequestNeeded(); | |
371 const auto& kPolicy = AffiliationFetchThrottler::kBackoffPolicy; | |
372 const int64_t& kGraceMs = | |
373 AffiliationFetchThrottler::kGracePeriodAfterReconnectMs; | |
374 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween( | |
375 true, kGraceMs * (1 - kPolicy.jitter_factor), kGraceMs)); | |
376 throttler->InformOfNetworkRequestComplete(true); | |
377 } | |
378 | |
379 TEST_F(AffiliationFetchThrottlerTest, | |
380 ConnectivityLostAndRestoredDuringRequest2) { | |
381 scoped_ptr<AffiliationFetchThrottler> throttler(CreateThrottler()); | |
382 | |
383 throttler->SignalNetworkRequestNeeded(); | |
384 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0)); | |
385 | |
386 SimulateHasNetworkConnectivity(false); | |
387 AssertNoReleaseUntilNoTasksRemain(); | |
388 SimulateHasNetworkConnectivity(true); | |
389 | |
390 const int64_t& kGraceMs = | |
391 AffiliationFetchThrottler::kGracePeriodAfterReconnectMs; | |
392 AssertNoReleaseForSecs(kGraceMs / base::Time::kMillisecondsPerSecond); | |
393 throttler->InformOfNetworkRequestComplete(true); | |
394 | |
395 // The next request should not be held back. | |
396 throttler->SignalNetworkRequestNeeded(); | |
397 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0)); | |
398 } | |
399 | |
400 TEST_F(AffiliationFetchThrottlerTest, InstanceDestroyedWhileInBackoff) { | |
401 scoped_ptr<AffiliationFetchThrottler> throttler(CreateThrottler()); | |
402 | |
403 throttler->SignalNetworkRequestNeeded(); | |
404 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0)); | |
405 throttler->InformOfNetworkRequestComplete(false); | |
406 | |
407 throttler->SignalNetworkRequestNeeded(); | |
408 throttler.reset(); | |
409 EXPECT_EQ(1u, GetPendingTaskCount()); | |
410 AssertNoReleaseUntilNoTasksRemain(); | |
411 } | |
412 | |
413 } // namespace password_manager | |
OLD | NEW |