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

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 most unittest 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
« 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 MockAffiliationFetchThrottlerDelegate(scoped_ptr<base::TickClock> tick_clock)
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; }
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 class MockNetworkChangeNotifier : public net::NetworkChangeNotifier {
mmenke 2015/01/26 18:49:13 This class isn't needed. See NetworkChangeNotifie
engedy 2015/01/26 19:39:35 Done.
58 public:
59 MockNetworkChangeNotifier() : connection_type_(CONNECTION_UNKNOWN) {}
60
61 void set_connection_type(ConnectionType connection_type) {
62 connection_type_ = connection_type;
63 NotifyObserversOfConnectionTypeChangeForTests(connection_type_);
64 }
65
66 ConnectionType GetCurrentConnectionType() const override {
67 return connection_type_;
68 }
69
70 private:
71 ConnectionType connection_type_;
72 };
73
74 // Rounds the provided time delta in milliseconds (potentially having fractional
75 // part) to the nearest microsecond and returns it.
76 base::TimeDelta RoundToNearestMicrosecond(double delta_ms) {
77 base::internal::CheckedNumeric<int64_t> delta_us(
78 delta_ms * base::Time::kMicrosecondsPerMillisecond + .5);
79 return base::TimeDelta::FromMicroseconds(delta_us.ValueOrDie());
80 }
81
82 } // namespace
83
84 class AffiliationFetchThrottlerTest : public testing::Test {
85 public:
86 AffiliationFetchThrottlerTest()
87 : task_runner_(new base::TestMockTimeTaskRunner),
88 mock_delegate_(task_runner_->GetMockTickClock()) {}
89 ~AffiliationFetchThrottlerTest() override {}
90
91 scoped_ptr<AffiliationFetchThrottler> CreateThrottler() {
92 return make_scoped_ptr(new AffiliationFetchThrottler(
93 &mock_delegate_, task_runner_, task_runner_->GetMockTickClock()));
94 }
95
96 void SimulateHasNetworkConnectivity(bool has_connectivity) {
97 network_change_notifier_.set_connection_type(
98 has_connectivity ? net::NetworkChangeNotifier::CONNECTION_UNKNOWN
99 : net::NetworkChangeNotifier::CONNECTION_NONE);
100 base::RunLoop().RunUntilIdle();
101 }
102
103 // Asserts that OnCanSendNetworkRequest() will have been called once by the
104 // time no tasks remain in the task runner, with an expected delay of at least
105 // |min_delay_ms| but no more than |max_delay_ms|. When called, the mock will
106 // return |emulated_return_value|, a boolean value normally indicating whether
107 // or not a request was actually issued in response to the call.
108 void AssertReleaseInBetween(bool emulated_return_value,
109 double min_delay_ms,
110 double max_delay_ms) {
111 ASSERT_EQ(0u, mock_delegate_.release_count());
112 base::TimeTicks ticks_at_start = task_runner_->GetCurrentMockTime();
113 mock_delegate_.set_emulated_return_value(emulated_return_value);
114 task_runner_->FastForwardUntilNoTasksRemain();
115 base::TimeDelta delay = mock_delegate_.last_release_time() - ticks_at_start;
116 ASSERT_EQ(1u, mock_delegate_.release_count());
117 EXPECT_LE(RoundToNearestMicrosecond(min_delay_ms), delay);
118 EXPECT_GE(RoundToNearestMicrosecond(max_delay_ms), delay);
mmenke 2015/01/26 18:49:13 Per comment, it may theoretically be possible for
engedy 2015/01/26 19:39:34 The reasons that it should never occur is because
mmenke 2015/01/30 19:50:41 Hrm...I thought I checked and it was truncated...M
119 mock_delegate_.reset_release_count();
120 }
121
122 // Runs the task runner for |ms| and asserts that OnCanSendNetworkRequest()
123 // will not have been called by the end of this period.
124 void AssertNoReleaseForMs(double ms) {
125 task_runner_->FastForwardBy(RoundToNearestMicrosecond(ms));
126 ASSERT_EQ(0u, mock_delegate_.release_count());
127 }
128
129 // Runs the task runner until no tasks remain, and asserts that
130 // OnCanSendNetworkRequest() will not have been called.
131 void AssertNoReleaseUntilNoTasksRemain() {
132 task_runner_->FastForwardUntilNoTasksRemain();
133 ASSERT_EQ(0u, mock_delegate_.release_count());
134 }
135
136 size_t GetPendingTaskCount() const {
137 return task_runner_->GetPendingTaskCount();
138 }
139
140 private:
141 // Needed because NetworkChangeNotifier uses ObserverList, which notifies
142 // observers on the MessageLoop that belongs to the thread from which they
143 // have registered.
144 base::MessageLoop message_loop_;
145 MockNetworkChangeNotifier network_change_notifier_;
146 scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
147 MockAffiliationFetchThrottlerDelegate mock_delegate_;
148
149 DISALLOW_COPY_AND_ASSIGN(AffiliationFetchThrottlerTest);
150 };
151
152 TEST_F(AffiliationFetchThrottlerTest, SuccessfulRequests) {
153 scoped_ptr<AffiliationFetchThrottler> throttler(CreateThrottler());
154
155 throttler->SignalNetworkRequestNeeded();
156 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
157
158 // Signal while request is in flight should be ignored.
159 throttler->SignalNetworkRequestNeeded();
160 AssertNoReleaseUntilNoTasksRemain();
161 throttler->InformOfNetworkRequestComplete(true);
162 AssertNoReleaseUntilNoTasksRemain();
163
164 // Duplicate the second signal 3 times: still only 1 callback should arrive.
165 throttler->SignalNetworkRequestNeeded();
166 throttler->SignalNetworkRequestNeeded();
167 throttler->SignalNetworkRequestNeeded();
168 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
169 }
170
171 TEST_F(AffiliationFetchThrottlerTest, FailedRequests) {
172 scoped_ptr<AffiliationFetchThrottler> throttler(CreateThrottler());
173
174 throttler->SignalNetworkRequestNeeded();
175 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
176 throttler->InformOfNetworkRequestComplete(false);
177
178 // The request after the first failure should be delayed by |initial_delay_ms|
179 // spread out over Uniform(1 - |jitter_factor|, 1).
180 throttler->SignalNetworkRequestNeeded();
181 const auto& kP = AffiliationFetchThrottler::kBackoffPolicy;
mmenke 2015/01/26 18:49:13 Per Google style guide, uncommon abbreviations sho
engedy 2015/01/26 19:39:35 Done.
182 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(
183 true, kP.initial_delay_ms * (1 - kP.jitter_factor), kP.initial_delay_ms));
184 throttler->InformOfNetworkRequestComplete(true);
185
186 // After a successful request, the next one should be released immediately.
187 throttler->SignalNetworkRequestNeeded();
188 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
189 throttler->InformOfNetworkRequestComplete(false);
190
191 // In general, the request after the n-th failure should be delayed by
192 // |multiply_factor| ^ (n-1) * |initial_delay_ms|,
193 // spread out over Uniform(1 - |jitter_factor|, 1), up until
194 // |maximum_backoff_ms|
195 // is reached.
196 for (size_t num_failures = 1; num_failures < 100; ++num_failures) {
197 throttler->SignalNetworkRequestNeeded();
198 double max_delay_ms =
199 kP.initial_delay_ms * pow(kP.multiply_factor, num_failures - 1);
200 double min_delay_ms = max_delay_ms * (1 - kP.jitter_factor);
201 if (max_delay_ms > kP.maximum_backoff_ms)
202 max_delay_ms = kP.maximum_backoff_ms;
203 if (min_delay_ms > kP.maximum_backoff_ms)
204 min_delay_ms = kP.maximum_backoff_ms;
205 ASSERT_NO_FATAL_FAILURE(
206 AssertReleaseInBetween(true, min_delay_ms, max_delay_ms));
207 throttler->InformOfNetworkRequestComplete(false);
208 }
209 }
210
211 TEST_F(AffiliationFetchThrottlerTest, OnCanSendNetworkRequestReturnsFalse) {
212 scoped_ptr<AffiliationFetchThrottler> throttler(CreateThrottler());
213
214 // A need for a network request is signaled, but as OnCanSendNetworkRequest()
215 // is called, the implementation returns false to indicate that the request
216 // will not be needed after all. InformOfNetworkRequestComplete() must not be
217 // called in this case.
218 throttler->SignalNetworkRequestNeeded();
219 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(false, 0, 0));
220
221 // A subsequent signaling, however, should result in OnCanSendNetworkRequest()
222 // being called immediately.
223 throttler->SignalNetworkRequestNeeded();
224 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
225 }
226
227 TEST_F(AffiliationFetchThrottlerTest, GracePeriodAfterConnectivityIsRestored) {
228 SimulateHasNetworkConnectivity(false);
229 scoped_ptr<AffiliationFetchThrottler> throttler(CreateThrottler());
230
231 // After connectivity is restored, the first request should be delayed by the
232 // grace period, spread out over Uniform(1 - |jitter_factor|, 1).
233 throttler->SignalNetworkRequestNeeded();
234 AssertNoReleaseUntilNoTasksRemain();
235
236 SimulateHasNetworkConnectivity(true);
237 const auto& kP = AffiliationFetchThrottler::kBackoffPolicy;
238 const int64_t& kGraceMs =
239 AffiliationFetchThrottler::kGracePeriodAfterReconnectMs;
240 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(
241 true, kGraceMs * (1 - kP.jitter_factor), kGraceMs));
242 throttler->InformOfNetworkRequestComplete(true);
243
244 // The next request should not be delayed.
245 throttler->SignalNetworkRequestNeeded();
246 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
247 }
248
249 // Same as GracePeriodAfterConnectivityIsRestored, but the network comes back
250 // just before SignalNetworkRequestNeeded() is called.
251 TEST_F(AffiliationFetchThrottlerTest, GracePeriodAfterConnectivityIsRestored2) {
252 SimulateHasNetworkConnectivity(false);
253 scoped_ptr<AffiliationFetchThrottler> throttler(CreateThrottler());
254
255 SimulateHasNetworkConnectivity(true);
256 throttler->SignalNetworkRequestNeeded();
257 const auto& kP = AffiliationFetchThrottler::kBackoffPolicy;
258 const int64_t& kGraceMs =
259 AffiliationFetchThrottler::kGracePeriodAfterReconnectMs;
260 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(
261 true, kGraceMs * (1 - kP.jitter_factor), kGraceMs));
262 throttler->InformOfNetworkRequestComplete(true);
263
264 throttler->SignalNetworkRequestNeeded();
265 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
266 }
267
268 // Same as GracePeriodAfterConnectivityIsRestored, but the network goes down
269 // just as SignalNetworkRequestNeeded() is constructed.
270 TEST_F(AffiliationFetchThrottlerTest, GracePeriodAfterConnectivityIsRestored3) {
271 scoped_ptr<AffiliationFetchThrottler> throttler(CreateThrottler());
272 SimulateHasNetworkConnectivity(false);
273
274 throttler->SignalNetworkRequestNeeded();
275 AssertNoReleaseUntilNoTasksRemain();
276
277 SimulateHasNetworkConnectivity(true);
278 const auto& kP = AffiliationFetchThrottler::kBackoffPolicy;
279 const int64_t& kGraceMs =
280 AffiliationFetchThrottler::kGracePeriodAfterReconnectMs;
281 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(
282 true, kGraceMs * (1 - kP.jitter_factor), kGraceMs));
283 throttler->InformOfNetworkRequestComplete(true);
284
285 throttler->SignalNetworkRequestNeeded();
286 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
287 }
288
289 TEST_F(AffiliationFetchThrottlerTest, ConnectivityLostDuringBackoff) {
290 scoped_ptr<AffiliationFetchThrottler> throttler(CreateThrottler());
291
292 throttler->SignalNetworkRequestNeeded();
293 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
294 throttler->InformOfNetworkRequestComplete(false);
295
296 throttler->SignalNetworkRequestNeeded();
297 SimulateHasNetworkConnectivity(false);
298
299 // Let the exponential backoff delay expire, and verify nothing happens.
300 AssertNoReleaseUntilNoTasksRemain();
301
302 // Verify that the request is, however, sent after the normal grace period
303 // once connectivity is restored.
304 SimulateHasNetworkConnectivity(true);
305 const auto& kP = AffiliationFetchThrottler::kBackoffPolicy;
306 const int64_t& kGraceMs =
307 AffiliationFetchThrottler::kGracePeriodAfterReconnectMs;
308 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(
309 true, kGraceMs * (1 - kP.jitter_factor), kGraceMs));
310 throttler->InformOfNetworkRequestComplete(true);
311 }
312
313 TEST_F(AffiliationFetchThrottlerTest,
314 ConnectivityLostAndRestoredDuringBackoff) {
315 scoped_ptr<AffiliationFetchThrottler> throttler(CreateThrottler());
316
317 throttler->SignalNetworkRequestNeeded();
318 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
319 throttler->InformOfNetworkRequestComplete(false);
320
321 // Simulate 10 flakes, and run for a total of 5 grace periods. This verifies
322 // that a flaky connection will not flood the task queue with lots of of tasks
323 // and also that release will not happen while the connection is flaky even
324 // once the first grace period has expired.
325 throttler->SignalNetworkRequestNeeded();
326 const auto& kP = AffiliationFetchThrottler::kBackoffPolicy;
327 const int64_t& kGraceMs =
328 AffiliationFetchThrottler::kGracePeriodAfterReconnectMs;
329 for (size_t t = 1; t <= 10; ++t) {
330 SimulateHasNetworkConnectivity(false);
331 AssertNoReleaseForMs(kGraceMs * (1 - kP.jitter_factor) / 2);
332 SimulateHasNetworkConnectivity(true);
333 }
334 EXPECT_EQ(1u, GetPendingTaskCount());
335 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(
336 true, kGraceMs * (1 - kP.jitter_factor), kGraceMs));
337 }
338
339 TEST_F(AffiliationFetchThrottlerTest, ConnectivityLostDuringRequest) {
340 scoped_ptr<AffiliationFetchThrottler> throttler(CreateThrottler());
341
342 throttler->SignalNetworkRequestNeeded();
343 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
344
345 SimulateHasNetworkConnectivity(false);
346 AssertNoReleaseUntilNoTasksRemain();
347
348 throttler->InformOfNetworkRequestComplete(false);
349 AssertNoReleaseUntilNoTasksRemain();
350 throttler->SignalNetworkRequestNeeded();
351 AssertNoReleaseUntilNoTasksRemain();
352
353 SimulateHasNetworkConnectivity(true);
354
355 // Verify that the next request is released after the grace period once
356 // connectivity is restored.
357 const auto& kP = AffiliationFetchThrottler::kBackoffPolicy;
358 const int64_t& kGraceMs =
359 AffiliationFetchThrottler::kGracePeriodAfterReconnectMs;
360 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(
361 true, kGraceMs * (1 - kP.jitter_factor), kGraceMs));
362 throttler->InformOfNetworkRequestComplete(true);
363 }
364
365 TEST_F(AffiliationFetchThrottlerTest,
366 ConnectivityLostAndRestoredDuringRequest) {
367 scoped_ptr<AffiliationFetchThrottler> throttler(CreateThrottler());
368
369 throttler->SignalNetworkRequestNeeded();
370 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
371
372 SimulateHasNetworkConnectivity(false);
373 AssertNoReleaseUntilNoTasksRemain();
374 SimulateHasNetworkConnectivity(true);
375 throttler->InformOfNetworkRequestComplete(true);
376 AssertNoReleaseUntilNoTasksRemain();
377
378 // Even though the previous request succeeded, the next request should still
379 // be held back for the normal grace period after connection is restored.
380 throttler->SignalNetworkRequestNeeded();
381 const auto& kP = AffiliationFetchThrottler::kBackoffPolicy;
382 const int64_t& kGraceMs =
383 AffiliationFetchThrottler::kGracePeriodAfterReconnectMs;
384 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(
385 true, kGraceMs * (1 - kP.jitter_factor), kGraceMs));
386 throttler->InformOfNetworkRequestComplete(true);
387 }
388
389 TEST_F(AffiliationFetchThrottlerTest, InstanceDestroyedWhileInBackoff) {
390 scoped_ptr<AffiliationFetchThrottler> throttler(CreateThrottler());
391
392 throttler->SignalNetworkRequestNeeded();
393 ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween(true, 0, 0));
394 throttler->InformOfNetworkRequestComplete(false);
395
396 throttler->SignalNetworkRequestNeeded();
397 throttler.reset();
398 EXPECT_EQ(1u, GetPendingTaskCount());
399 AssertNoReleaseUntilNoTasksRemain();
400 }
401
402 } // 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