123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314 |
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "net/base/backoff_entry.h"
- #include "base/time/tick_clock.h"
- #include "base/time/time.h"
- #include "testing/gtest/include/gtest/gtest.h"
- namespace net {
- namespace {
- using base::TimeTicks;
- BackoffEntry::Policy base_policy = { 0, 1000, 2.0, 0.0, 20000, 2000, false };
- class TestTickClock : public base::TickClock {
- public:
- TestTickClock() = default;
- TestTickClock(const TestTickClock&) = delete;
- TestTickClock& operator=(const TestTickClock&) = delete;
- ~TestTickClock() override = default;
- TimeTicks NowTicks() const override { return now_ticks_; }
- void set_now(TimeTicks now) { now_ticks_ = now; }
- private:
- TimeTicks now_ticks_;
- };
- TEST(BackoffEntryTest, BaseTest) {
- TestTickClock now_ticks;
- BackoffEntry entry(&base_policy, &now_ticks);
- EXPECT_FALSE(entry.ShouldRejectRequest());
- EXPECT_EQ(base::TimeDelta(), entry.GetTimeUntilRelease());
- entry.InformOfRequest(false);
- EXPECT_TRUE(entry.ShouldRejectRequest());
- EXPECT_EQ(base::Milliseconds(1000), entry.GetTimeUntilRelease());
- }
- TEST(BackoffEntryTest, CanDiscardNeverExpires) {
- BackoffEntry::Policy never_expires_policy = base_policy;
- never_expires_policy.entry_lifetime_ms = -1;
- TestTickClock now_ticks;
- BackoffEntry never_expires(&never_expires_policy, &now_ticks);
- EXPECT_FALSE(never_expires.CanDiscard());
- now_ticks.set_now(TimeTicks() + base::Days(100));
- EXPECT_FALSE(never_expires.CanDiscard());
- }
- TEST(BackoffEntryTest, CanDiscard) {
- TestTickClock now_ticks;
- BackoffEntry entry(&base_policy, &now_ticks);
- // Because lifetime is non-zero, we shouldn't be able to discard yet.
- EXPECT_FALSE(entry.CanDiscard());
- // Test the "being used" case.
- entry.InformOfRequest(false);
- EXPECT_FALSE(entry.CanDiscard());
- // Test the case where there are errors but we can time out.
- now_ticks.set_now(entry.GetReleaseTime() + base::Milliseconds(1));
- EXPECT_FALSE(entry.CanDiscard());
- now_ticks.set_now(entry.GetReleaseTime() +
- base::Milliseconds(base_policy.maximum_backoff_ms + 1));
- EXPECT_TRUE(entry.CanDiscard());
- // Test the final case (no errors, dependent only on specified lifetime).
- now_ticks.set_now(entry.GetReleaseTime() +
- base::Milliseconds(base_policy.entry_lifetime_ms - 1));
- entry.InformOfRequest(true);
- EXPECT_FALSE(entry.CanDiscard());
- now_ticks.set_now(entry.GetReleaseTime() +
- base::Milliseconds(base_policy.entry_lifetime_ms));
- EXPECT_TRUE(entry.CanDiscard());
- }
- TEST(BackoffEntryTest, CanDiscardAlwaysDelay) {
- BackoffEntry::Policy always_delay_policy = base_policy;
- always_delay_policy.always_use_initial_delay = true;
- always_delay_policy.entry_lifetime_ms = 0;
- TestTickClock now_ticks;
- BackoffEntry entry(&always_delay_policy, &now_ticks);
- // Because lifetime is non-zero, we shouldn't be able to discard yet.
- now_ticks.set_now(entry.GetReleaseTime() + base::Milliseconds(2000));
- EXPECT_TRUE(entry.CanDiscard());
- // Even with no failures, we wait until the delay before we allow discard.
- entry.InformOfRequest(true);
- EXPECT_FALSE(entry.CanDiscard());
- // Wait until the delay expires, and we can discard the entry again.
- now_ticks.set_now(entry.GetReleaseTime() + base::Milliseconds(1000));
- EXPECT_TRUE(entry.CanDiscard());
- }
- TEST(BackoffEntryTest, CanDiscardNotStored) {
- BackoffEntry::Policy no_store_policy = base_policy;
- no_store_policy.entry_lifetime_ms = 0;
- TestTickClock now_ticks;
- BackoffEntry not_stored(&no_store_policy, &now_ticks);
- EXPECT_TRUE(not_stored.CanDiscard());
- }
- TEST(BackoffEntryTest, ShouldIgnoreFirstTwo) {
- BackoffEntry::Policy lenient_policy = base_policy;
- lenient_policy.num_errors_to_ignore = 2;
- BackoffEntry entry(&lenient_policy);
- entry.InformOfRequest(false);
- EXPECT_FALSE(entry.ShouldRejectRequest());
- entry.InformOfRequest(false);
- EXPECT_FALSE(entry.ShouldRejectRequest());
- entry.InformOfRequest(false);
- EXPECT_TRUE(entry.ShouldRejectRequest());
- }
- TEST(BackoffEntryTest, ReleaseTimeCalculation) {
- TestTickClock now_ticks;
- BackoffEntry entry(&base_policy, &now_ticks);
- // With zero errors, should return "now".
- TimeTicks result = entry.GetReleaseTime();
- EXPECT_EQ(now_ticks.NowTicks(), result);
- // 1 error.
- entry.InformOfRequest(false);
- result = entry.GetReleaseTime();
- EXPECT_EQ(now_ticks.NowTicks() + base::Milliseconds(1000), result);
- EXPECT_EQ(base::Milliseconds(1000), entry.GetTimeUntilRelease());
- // 2 errors.
- entry.InformOfRequest(false);
- result = entry.GetReleaseTime();
- EXPECT_EQ(now_ticks.NowTicks() + base::Milliseconds(2000), result);
- EXPECT_EQ(base::Milliseconds(2000), entry.GetTimeUntilRelease());
- // 3 errors.
- entry.InformOfRequest(false);
- result = entry.GetReleaseTime();
- EXPECT_EQ(now_ticks.NowTicks() + base::Milliseconds(4000), result);
- EXPECT_EQ(base::Milliseconds(4000), entry.GetTimeUntilRelease());
- // 6 errors (to check it doesn't pass maximum).
- entry.InformOfRequest(false);
- entry.InformOfRequest(false);
- entry.InformOfRequest(false);
- result = entry.GetReleaseTime();
- EXPECT_EQ(now_ticks.NowTicks() + base::Milliseconds(20000), result);
- }
- TEST(BackoffEntryTest, ReleaseTimeCalculationAlwaysDelay) {
- BackoffEntry::Policy always_delay_policy = base_policy;
- always_delay_policy.always_use_initial_delay = true;
- always_delay_policy.num_errors_to_ignore = 2;
- TestTickClock now_ticks;
- BackoffEntry entry(&always_delay_policy, &now_ticks);
- // With previous requests, should return "now".
- TimeTicks result = entry.GetReleaseTime();
- EXPECT_EQ(base::TimeDelta(), entry.GetTimeUntilRelease());
- // 1 error.
- entry.InformOfRequest(false);
- EXPECT_EQ(base::Milliseconds(1000), entry.GetTimeUntilRelease());
- // 2 errors.
- entry.InformOfRequest(false);
- EXPECT_EQ(base::Milliseconds(1000), entry.GetTimeUntilRelease());
- // 3 errors, exponential backoff starts.
- entry.InformOfRequest(false);
- EXPECT_EQ(base::Milliseconds(2000), entry.GetTimeUntilRelease());
- // 4 errors.
- entry.InformOfRequest(false);
- EXPECT_EQ(base::Milliseconds(4000), entry.GetTimeUntilRelease());
- // 8 errors (to check it doesn't pass maximum).
- entry.InformOfRequest(false);
- entry.InformOfRequest(false);
- entry.InformOfRequest(false);
- entry.InformOfRequest(false);
- result = entry.GetReleaseTime();
- EXPECT_EQ(base::Milliseconds(20000), entry.GetTimeUntilRelease());
- }
- TEST(BackoffEntryTest, ReleaseTimeCalculationWithJitter) {
- for (int i = 0; i < 10; ++i) {
- BackoffEntry::Policy jittery_policy = base_policy;
- jittery_policy.jitter_factor = 0.2;
- TestTickClock now_ticks;
- BackoffEntry entry(&jittery_policy, &now_ticks);
- entry.InformOfRequest(false);
- entry.InformOfRequest(false);
- entry.InformOfRequest(false);
- TimeTicks result = entry.GetReleaseTime();
- EXPECT_LE(now_ticks.NowTicks() + base::Milliseconds(3200), result);
- EXPECT_GE(now_ticks.NowTicks() + base::Milliseconds(4000), result);
- }
- }
- TEST(BackoffEntryTest, FailureThenSuccess) {
- TestTickClock now_ticks;
- BackoffEntry entry(&base_policy, &now_ticks);
- // Failure count 1, establishes horizon.
- entry.InformOfRequest(false);
- TimeTicks release_time = entry.GetReleaseTime();
- EXPECT_EQ(TimeTicks() + base::Milliseconds(1000), release_time);
- // Success, failure count 0, should not advance past
- // the horizon that was already set.
- now_ticks.set_now(release_time - base::Milliseconds(200));
- entry.InformOfRequest(true);
- EXPECT_EQ(release_time, entry.GetReleaseTime());
- // Failure, failure count 1.
- entry.InformOfRequest(false);
- EXPECT_EQ(release_time + base::Milliseconds(800), entry.GetReleaseTime());
- }
- TEST(BackoffEntryTest, FailureThenSuccessAlwaysDelay) {
- BackoffEntry::Policy always_delay_policy = base_policy;
- always_delay_policy.always_use_initial_delay = true;
- always_delay_policy.num_errors_to_ignore = 1;
- TestTickClock now_ticks;
- BackoffEntry entry(&always_delay_policy, &now_ticks);
- // Failure count 1.
- entry.InformOfRequest(false);
- EXPECT_EQ(base::Milliseconds(1000), entry.GetTimeUntilRelease());
- // Failure count 2.
- entry.InformOfRequest(false);
- EXPECT_EQ(base::Milliseconds(2000), entry.GetTimeUntilRelease());
- now_ticks.set_now(entry.GetReleaseTime() + base::Milliseconds(2000));
- // Success. We should go back to the original delay.
- entry.InformOfRequest(true);
- EXPECT_EQ(base::Milliseconds(1000), entry.GetTimeUntilRelease());
- // Failure count reaches 2 again. We should increase the delay once more.
- entry.InformOfRequest(false);
- EXPECT_EQ(base::Milliseconds(2000), entry.GetTimeUntilRelease());
- now_ticks.set_now(entry.GetReleaseTime() + base::Milliseconds(2000));
- }
- TEST(BackoffEntryTest, RetainCustomHorizon) {
- TestTickClock now_ticks;
- BackoffEntry custom(&base_policy, &now_ticks);
- TimeTicks custom_horizon = TimeTicks() + base::Days(3);
- custom.SetCustomReleaseTime(custom_horizon);
- custom.InformOfRequest(false);
- custom.InformOfRequest(true);
- now_ticks.set_now(TimeTicks() + base::Days(2));
- custom.InformOfRequest(false);
- custom.InformOfRequest(true);
- EXPECT_EQ(custom_horizon, custom.GetReleaseTime());
- // Now check that once we are at or past the custom horizon,
- // we get normal behavior.
- now_ticks.set_now(TimeTicks() + base::Days(3));
- custom.InformOfRequest(false);
- EXPECT_EQ(TimeTicks() + base::Days(3) + base::Milliseconds(1000),
- custom.GetReleaseTime());
- }
- TEST(BackoffEntryTest, RetainCustomHorizonWhenInitialErrorsIgnored) {
- // Regression test for a bug discovered during code review.
- BackoffEntry::Policy lenient_policy = base_policy;
- lenient_policy.num_errors_to_ignore = 1;
- TestTickClock now_ticks;
- BackoffEntry custom(&lenient_policy, &now_ticks);
- TimeTicks custom_horizon = TimeTicks() + base::Days(3);
- custom.SetCustomReleaseTime(custom_horizon);
- custom.InformOfRequest(false); // This must not reset the horizon.
- EXPECT_EQ(custom_horizon, custom.GetReleaseTime());
- }
- TEST(BackoffEntryTest, OverflowProtection) {
- BackoffEntry::Policy large_multiply_policy = base_policy;
- large_multiply_policy.multiply_factor = 256;
- TestTickClock now_ticks;
- BackoffEntry custom(&large_multiply_policy, &now_ticks);
- // Trigger enough failures such that more than 11 bits of exponent are used
- // to represent the exponential backoff intermediate values. Given a multiply
- // factor of 256 (2^8), 129 iterations is enough: 2^(8*(129-1)) = 2^1024.
- for (int i = 0; i < 129; ++i) {
- now_ticks.set_now(now_ticks.NowTicks() + custom.GetTimeUntilRelease());
- custom.InformOfRequest(false);
- ASSERT_TRUE(custom.ShouldRejectRequest());
- }
- // Max delay should still be respected.
- EXPECT_EQ(20000, custom.GetTimeUntilRelease().InMilliseconds());
- }
- } // namespace
- } // namespace net
|