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 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 |
OLD | NEW |