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

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 header guards. Created 6 years 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 2014 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 <queue>
6
7 #include "base/callback.h"
8 #include "base/memory/ref_counted.h"
9 #include "base/run_loop.h"
10 #include "base/single_thread_task_runner.h"
11 #include "base/thread_task_runner_handle.h"
12 #include "base/time/tick_clock.h"
13 #include "base/time/time.h"
14 #include "components/password_manager/core/browser/affiliation_fetch_throttler.h "
15 #include "testing/gmock/include/gmock/gmock.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17
18 namespace password_manager {
19 namespace {
20
21 // A SingleThreadTaskRunner that mocks the current time and allows it to be
22 // fast-forwarded. TODO(bartfab): Copies of this class exist in several tests.
23 // Consolidate them (crbug.com/329911).
24 class MockTimeSingleThreadTaskRunner : public base::SingleThreadTaskRunner {
25 public:
26 MockTimeSingleThreadTaskRunner();
27
28 // base::SingleThreadTaskRunner:
29 virtual bool RunsTasksOnCurrentThread() const override;
30 virtual bool PostDelayedTask(const tracked_objects::Location& from_here,
31 const base::Closure& task,
32 base::TimeDelta delay) override;
33 virtual bool PostNonNestableDelayedTask(
34 const tracked_objects::Location& from_here,
35 const base::Closure& task,
36 base::TimeDelta delay) override;
37
38 const base::TimeTicks& GetCurrentTime() const;
39
40 size_t task_queue_size() const {
41 return tasks_.size();
42 }
43
44 void FastForwardBy(base::TimeDelta delta);
45 void FastForwardUntilNoTasksRemain();
46
47 private:
48 // Strict weak temporal ordering of tasks.
49 class TemporalOrder {
50 public:
51 bool operator()(
52 const std::pair<base::TimeTicks, base::Closure>& first_task,
53 const std::pair<base::TimeTicks, base::Closure>& second_task) const;
54 };
55
56 virtual ~MockTimeSingleThreadTaskRunner();
57
58 base::TimeTicks now_;
59 std::priority_queue<std::pair<base::TimeTicks, base::Closure>,
60 std::vector<std::pair<base::TimeTicks, base::Closure>>,
61 TemporalOrder> tasks_;
62
63 DISALLOW_COPY_AND_ASSIGN(MockTimeSingleThreadTaskRunner);
64 };
65
66 // A base::TickClock that uses a MockTimeSingleThreadTaskRunner as the source of
67 // the current time.
68 class MockClock : public base::TickClock {
69 public:
70 explicit MockClock(scoped_refptr<MockTimeSingleThreadTaskRunner> task_runner);
71 virtual ~MockClock();
72
73 // base::TickClock:
74 virtual base::TimeTicks NowTicks() override;
75
76 private:
77 scoped_refptr<MockTimeSingleThreadTaskRunner> task_runner_;
78
79 DISALLOW_COPY_AND_ASSIGN(MockClock);
80 };
81
82 MockTimeSingleThreadTaskRunner::MockTimeSingleThreadTaskRunner() {
83 }
84
85 bool MockTimeSingleThreadTaskRunner::RunsTasksOnCurrentThread() const {
86 return true;
87 }
88
89 bool MockTimeSingleThreadTaskRunner::PostDelayedTask(
90 const tracked_objects::Location& from_here,
91 const base::Closure& task,
92 base::TimeDelta delay) {
93 tasks_.push(std::make_pair(now_ + delay, task));
94 return true;
95 }
96
97 bool MockTimeSingleThreadTaskRunner::PostNonNestableDelayedTask(
98 const tracked_objects::Location& from_here,
99 const base::Closure& task,
100 base::TimeDelta delay) {
101 NOTREACHED();
102 return false;
103 }
104
105 const base::TimeTicks& MockTimeSingleThreadTaskRunner::GetCurrentTime() const {
106 return now_;
107 }
108
109 void MockTimeSingleThreadTaskRunner::FastForwardBy(base::TimeDelta delta) {
110 const base::TimeTicks latest = now_ + delta;
111 while (!tasks_.empty() && tasks_.top().first <= latest) {
112 now_ = tasks_.top().first;
113 base::Closure task = tasks_.top().second;
114 tasks_.pop();
115 task.Run();
116 }
117 now_ = latest;
118 }
119
120 void MockTimeSingleThreadTaskRunner::FastForwardUntilNoTasksRemain() {
121 while (!tasks_.empty()) {
122 now_ = tasks_.top().first;
123 base::Closure task = tasks_.top().second;
124 tasks_.pop();
125 task.Run();
126 }
127 }
128
129 bool MockTimeSingleThreadTaskRunner::TemporalOrder::operator()(
130 const std::pair<base::TimeTicks, base::Closure>& first_task,
131 const std::pair<base::TimeTicks, base::Closure>& second_task) const {
132 return first_task.first > second_task.first;
133 }
134
135 MockTimeSingleThreadTaskRunner::~MockTimeSingleThreadTaskRunner() {
136 }
137
138 MockClock::MockClock(scoped_refptr<MockTimeSingleThreadTaskRunner> task_runner)
139 : task_runner_(task_runner) {
140 }
141
142 MockClock::~MockClock() {
143 }
144
145 base::TimeTicks MockClock::NowTicks() {
146 return task_runner_->GetCurrentTime();
147 }
148
149 class MockAffiliationFetchThrottlerDelegate
150 : public testing::StrictMock<AffiliationFetchThrottlerDelegate> {
151 public:
152 MockAffiliationFetchThrottlerDelegate() {}
153 ~MockAffiliationFetchThrottlerDelegate() {}
154
155 MOCK_METHOD0(OnCanSendNetworkRequest, bool());
156
157 private:
158 DISALLOW_COPY_AND_ASSIGN(MockAffiliationFetchThrottlerDelegate);
159 };
160
161 class MockNetworkChangeNotifier : public net::NetworkChangeNotifier {
162 public:
163 MockNetworkChangeNotifier() : connection_type_(CONNECTION_UNKNOWN) {}
164
165 void set_connection_type(ConnectionType connection_type) {
166 connection_type_ = connection_type;
167 NotifyObserversOfConnectionTypeChangeForTests(connection_type_);
168 }
169
170 ConnectionType GetCurrentConnectionType() const override {
171 return connection_type_;
172 }
173
174 private:
175 ConnectionType connection_type_;
176 };
177
178 } // namespace
179
180 class AffiliationFetchThrottlerTest : public testing::Test {
181 public:
182 AffiliationFetchThrottlerTest()
183 : network_change_notifier_(new MockNetworkChangeNotifier),
184 task_runner_(new MockTimeSingleThreadTaskRunner) {}
185 ~AffiliationFetchThrottlerTest() override {}
186
187 void SetUp() override {
188 policy_.reset(new AffiliationFetchThrottler(
189 &mock_delegate_, task_runner_,
190 make_scoped_ptr(new MockClock(task_runner_))));
191 }
192
193 void SimulateHasNetworkConnectivity(bool has_connectivity) {
194 network_change_notifier_->set_connection_type(has_connectivity ?
195 net::NetworkChangeNotifier::CONNECTION_UNKNOWN :
196 net::NetworkChangeNotifier::CONNECTION_NONE);
197 base::RunLoop().RunUntilIdle();
198 }
199
200 // Forwards time, and given that MockAffiliationFetchThrottlerDelegate is a
201 // strict mock, also ensures OnCanSendNetworkRequest() is not called back
202 // during this period.
203 void FastForwardTimeBySecs(double secs) {
204 task_runner_->FastForwardBy(base::TimeDelta::FromSecondsD(secs));
205 }
206
207 void FastForwardUntilNoTasksRemain() {
208 task_runner_->FastForwardUntilNoTasksRemain();
209 }
210
211 size_t NumberOfEnqueuedTasks() {
212 return task_runner_->task_queue_size();
213 }
214
215 // Forwards time until the next OnCanSendNetworkRequest() callback, and
216 // verifies that it occurs between |min_delay_sec| and |max_delay_sec| seconds
217 // from now, inclusive.
218 void AssertReleaseBetweenSecs(double min_delay_sec, double max_delay_sec) {
219 base::TimeTicks ticks_at_start = task_runner_->GetCurrentTime();
220 base::TimeDelta min_delay = base::TimeDelta::FromSecondsD(min_delay_sec);
221 if (min_delay.ToInternalValue() > 0) {
222 task_runner_->FastForwardBy(min_delay -
223 base::TimeDelta::FromInternalValue(1));
224 }
225 EXPECT_CALL(mock_delegate_, OnCanSendNetworkRequest())
226 .Times(1)
227 .WillRepeatedly(testing::Return(true));
228 task_runner_->FastForwardUntilNoTasksRemain();
229 ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(&mock_delegate_));
230 ASSERT_GE(base::TimeDelta::FromSecondsD(max_delay_sec),
231 task_runner_->GetCurrentTime() - ticks_at_start);
232 }
233
234 AffiliationFetchThrottler& policy() { return *policy_.get(); }
235
236 private:
237 // Needed because NetworkChangeNotifier uses ObserverList, which notifies
238 // observers on the MessageLoop that belongs to the thread from which they
239 // have registered.
240 base::MessageLoop message_loop_;
241 scoped_ptr<MockNetworkChangeNotifier> network_change_notifier_;
242 scoped_refptr<MockTimeSingleThreadTaskRunner> task_runner_;
243 MockAffiliationFetchThrottlerDelegate mock_delegate_;
244 scoped_ptr<AffiliationFetchThrottler> policy_;
245
246 DISALLOW_COPY_AND_ASSIGN(AffiliationFetchThrottlerTest);
247 };
248
249 TEST_F(AffiliationFetchThrottlerTest, SuccessfulRequests) {
250 SimulateHasNetworkConnectivity(true);
251
252 policy().SignalNetworkRequestNeeded();
253 AssertReleaseBetweenSecs(0, 0);
254
255 // Signal while request is in flight should be ignored.
256 policy().SignalNetworkRequestNeeded();
257 FastForwardUntilNoTasksRemain();
258 policy().InformOfNetworkRequestComplete(true);
259 FastForwardUntilNoTasksRemain();
260
261 // Duplicate the second signal 3 times: still only 1 callback should arrive.
262 policy().SignalNetworkRequestNeeded();
263 policy().SignalNetworkRequestNeeded();
264 policy().SignalNetworkRequestNeeded();
265 AssertReleaseBetweenSecs(0, 0);
266 }
267
268 TEST_F(AffiliationFetchThrottlerTest, FailedRequests) {
269 SimulateHasNetworkConnectivity(true);
270
271 policy().SignalNetworkRequestNeeded();
272 ASSERT_NO_FATAL_FAILURE(AssertReleaseBetweenSecs(0, 0));
273 policy().InformOfNetworkRequestComplete(false);
274
275 // Request after first failure should be delayed by 10 seconds % 50% fuzzing
276 // factor.
277 policy().SignalNetworkRequestNeeded();
278 ASSERT_NO_FATAL_FAILURE(AssertReleaseBetweenSecs(5, 10));
279 policy().InformOfNetworkRequestComplete(false);
280
281 // Request after first failure should be delayed by 40 seconds % 50% fuzzing
282 // factor, applied twice.
283 policy().SignalNetworkRequestNeeded();
284 ASSERT_NO_FATAL_FAILURE(AssertReleaseBetweenSecs(10, 40));
285 policy().InformOfNetworkRequestComplete(false);
286 }
287
288 TEST_F(AffiliationFetchThrottlerTest, GracePeriodAfterConnectivityIsRestored) {
289 SimulateHasNetworkConnectivity(false);
290
291 // After connectivity is re-established, the first request should be delayed
292 // by 10 seconds % 50% fuzzing factor.
293 policy().SignalNetworkRequestNeeded();
294 SimulateHasNetworkConnectivity(true);
295 ASSERT_NO_FATAL_FAILURE(AssertReleaseBetweenSecs(5, 10));
296 policy().InformOfNetworkRequestComplete(true);
297
298 // The next request should not be delayed.
299 policy().SignalNetworkRequestNeeded();
300 AssertReleaseBetweenSecs(0, 0);
301 }
302
303 // Same as GracePeriodAfterConnectivityIsRestored, but the network comes back
304 // just before SignalNetworkRequestNeeded() is called.
305 TEST_F(AffiliationFetchThrottlerTest,
306 GracePeriodAfterConnectivityIsRestored2) {
307 SimulateHasNetworkConnectivity(false);
308
309 SimulateHasNetworkConnectivity(true);
310 policy().SignalNetworkRequestNeeded();
311 ASSERT_NO_FATAL_FAILURE(AssertReleaseBetweenSecs(5, 10));
312 policy().InformOfNetworkRequestComplete(true);
313
314 policy().SignalNetworkRequestNeeded();
315 AssertReleaseBetweenSecs(0, 0);
316 }
317
318 // TODO(engedy): This test is flaky. Investigate.
319 TEST_F(AffiliationFetchThrottlerTest, ConnectivityLostDuringBackoff) {
320 SimulateHasNetworkConnectivity(true);
321
322 policy().SignalNetworkRequestNeeded();
323 ASSERT_NO_FATAL_FAILURE(AssertReleaseBetweenSecs(0, 0));
324 policy().InformOfNetworkRequestComplete(false);
325
326 policy().SignalNetworkRequestNeeded();
327 SimulateHasNetworkConnectivity(false);
328
329 // Let the exponential backoff delay expire, and verify nothing happens.
330 FastForwardUntilNoTasksRemain();
331
332 // Verify that the request is, however, sent after 10 seconds % 50 fuzzing
333 // factor.
334 SimulateHasNetworkConnectivity(true);
335 ASSERT_NO_FATAL_FAILURE(AssertReleaseBetweenSecs(5, 10));
336 }
337
338 TEST_F(AffiliationFetchThrottlerTest,
339 ConnectivityLostAndRestoredDuringBackoff) {
340 SimulateHasNetworkConnectivity(true);
341
342 policy().SignalNetworkRequestNeeded();
343 ASSERT_NO_FATAL_FAILURE(AssertReleaseBetweenSecs(0, 0));
344 policy().InformOfNetworkRequestComplete(false);
345
346 policy().SignalNetworkRequestNeeded();
347
348 // Also verify that a flaky connection will not flood the task queue with lots
349 // of tasks.
350 for (size_t t = 1; t <= 5; ++t) {
351 SimulateHasNetworkConnectivity(false);
352 SimulateHasNetworkConnectivity(true);
353 FastForwardTimeBySecs(1);
354 }
355 EXPECT_EQ(1u, NumberOfEnqueuedTasks());
356
357 ASSERT_NO_FATAL_FAILURE(AssertReleaseBetweenSecs(5, 10));
358 }
359
360
361 } // namespace password_manager
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698