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

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

Powered by Google App Engine
This is Rietveld 408576698