Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(120)

Side by Side Diff: components/password_manager/core/browser/affiliation_fetch_throttler_unittest.cc

Issue 807503002: Implement throttling logic for fetching affiliation information. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@aff_database
Patch Set: Addressed more comments. Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698