1 // Copyright (c) 2012 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.
5 #include "net/base/backoff_entry.h"
7 #include "base/macros.h"
8 #include "base/time/tick_clock.h"
9 #include "testing/gtest/include/gtest/gtest.h"
15 using base::TimeDelta
;
16 using base::TimeTicks
;
18 BackoffEntry::Policy base_policy
= { 0, 1000, 2.0, 0.0, 20000, 2000, false };
20 class TestTickClock
: public base::TickClock
{
23 ~TestTickClock() override
{}
25 TimeTicks
NowTicks() override
{ return now_ticks_
; }
26 void set_now(TimeTicks now
) { now_ticks_
= now
; }
30 DISALLOW_COPY_AND_ASSIGN(TestTickClock
);
33 TEST(BackoffEntryTest
, BaseTest
) {
34 TestTickClock now_ticks
;
35 BackoffEntry
entry(&base_policy
, &now_ticks
);
36 EXPECT_FALSE(entry
.ShouldRejectRequest());
37 EXPECT_EQ(TimeDelta(), entry
.GetTimeUntilRelease());
39 entry
.InformOfRequest(false);
40 EXPECT_TRUE(entry
.ShouldRejectRequest());
41 EXPECT_EQ(TimeDelta::FromMilliseconds(1000), entry
.GetTimeUntilRelease());
44 TEST(BackoffEntryTest
, CanDiscardNeverExpires
) {
45 BackoffEntry::Policy never_expires_policy
= base_policy
;
46 never_expires_policy
.entry_lifetime_ms
= -1;
47 TestTickClock now_ticks
;
48 BackoffEntry
never_expires(&never_expires_policy
, &now_ticks
);
49 EXPECT_FALSE(never_expires
.CanDiscard());
50 now_ticks
.set_now(TimeTicks() + TimeDelta::FromDays(100));
51 EXPECT_FALSE(never_expires
.CanDiscard());
54 TEST(BackoffEntryTest
, CanDiscard
) {
55 TestTickClock now_ticks
;
56 BackoffEntry
entry(&base_policy
, &now_ticks
);
57 // Because lifetime is non-zero, we shouldn't be able to discard yet.
58 EXPECT_FALSE(entry
.CanDiscard());
60 // Test the "being used" case.
61 entry
.InformOfRequest(false);
62 EXPECT_FALSE(entry
.CanDiscard());
64 // Test the case where there are errors but we can time out.
65 now_ticks
.set_now(entry
.GetReleaseTime() + TimeDelta::FromMilliseconds(1));
66 EXPECT_FALSE(entry
.CanDiscard());
67 now_ticks
.set_now(entry
.GetReleaseTime() + TimeDelta::FromMilliseconds(
68 base_policy
.maximum_backoff_ms
+ 1));
69 EXPECT_TRUE(entry
.CanDiscard());
71 // Test the final case (no errors, dependent only on specified lifetime).
72 now_ticks
.set_now(entry
.GetReleaseTime() + TimeDelta::FromMilliseconds(
73 base_policy
.entry_lifetime_ms
- 1));
74 entry
.InformOfRequest(true);
75 EXPECT_FALSE(entry
.CanDiscard());
76 now_ticks
.set_now(entry
.GetReleaseTime() + TimeDelta::FromMilliseconds(
77 base_policy
.entry_lifetime_ms
));
78 EXPECT_TRUE(entry
.CanDiscard());
81 TEST(BackoffEntryTest
, CanDiscardAlwaysDelay
) {
82 BackoffEntry::Policy always_delay_policy
= base_policy
;
83 always_delay_policy
.always_use_initial_delay
= true;
84 always_delay_policy
.entry_lifetime_ms
= 0;
86 TestTickClock now_ticks
;
87 BackoffEntry
entry(&always_delay_policy
, &now_ticks
);
89 // Because lifetime is non-zero, we shouldn't be able to discard yet.
90 now_ticks
.set_now(entry
.GetReleaseTime() + TimeDelta::FromMilliseconds(2000));
91 EXPECT_TRUE(entry
.CanDiscard());
93 // Even with no failures, we wait until the delay before we allow discard.
94 entry
.InformOfRequest(true);
95 EXPECT_FALSE(entry
.CanDiscard());
97 // Wait until the delay expires, and we can discard the entry again.
98 now_ticks
.set_now(entry
.GetReleaseTime() + TimeDelta::FromMilliseconds(1000));
99 EXPECT_TRUE(entry
.CanDiscard());
102 TEST(BackoffEntryTest
, CanDiscardNotStored
) {
103 BackoffEntry::Policy no_store_policy
= base_policy
;
104 no_store_policy
.entry_lifetime_ms
= 0;
105 TestTickClock now_ticks
;
106 BackoffEntry
not_stored(&no_store_policy
, &now_ticks
);
107 EXPECT_TRUE(not_stored
.CanDiscard());
110 TEST(BackoffEntryTest
, ShouldIgnoreFirstTwo
) {
111 BackoffEntry::Policy lenient_policy
= base_policy
;
112 lenient_policy
.num_errors_to_ignore
= 2;
114 BackoffEntry
entry(&lenient_policy
);
116 entry
.InformOfRequest(false);
117 EXPECT_FALSE(entry
.ShouldRejectRequest());
119 entry
.InformOfRequest(false);
120 EXPECT_FALSE(entry
.ShouldRejectRequest());
122 entry
.InformOfRequest(false);
123 EXPECT_TRUE(entry
.ShouldRejectRequest());
126 TEST(BackoffEntryTest
, ReleaseTimeCalculation
) {
127 TestTickClock now_ticks
;
128 BackoffEntry
entry(&base_policy
, &now_ticks
);
130 // With zero errors, should return "now".
131 TimeTicks result
= entry
.GetReleaseTime();
132 EXPECT_EQ(now_ticks
.NowTicks(), result
);
135 entry
.InformOfRequest(false);
136 result
= entry
.GetReleaseTime();
137 EXPECT_EQ(now_ticks
.NowTicks() + TimeDelta::FromMilliseconds(1000), result
);
138 EXPECT_EQ(TimeDelta::FromMilliseconds(1000), entry
.GetTimeUntilRelease());
141 entry
.InformOfRequest(false);
142 result
= entry
.GetReleaseTime();
143 EXPECT_EQ(now_ticks
.NowTicks() + TimeDelta::FromMilliseconds(2000), result
);
144 EXPECT_EQ(TimeDelta::FromMilliseconds(2000), entry
.GetTimeUntilRelease());
147 entry
.InformOfRequest(false);
148 result
= entry
.GetReleaseTime();
149 EXPECT_EQ(now_ticks
.NowTicks() + TimeDelta::FromMilliseconds(4000), result
);
150 EXPECT_EQ(TimeDelta::FromMilliseconds(4000), entry
.GetTimeUntilRelease());
152 // 6 errors (to check it doesn't pass maximum).
153 entry
.InformOfRequest(false);
154 entry
.InformOfRequest(false);
155 entry
.InformOfRequest(false);
156 result
= entry
.GetReleaseTime();
157 EXPECT_EQ(now_ticks
.NowTicks() + TimeDelta::FromMilliseconds(20000), result
);
160 TEST(BackoffEntryTest
, ReleaseTimeCalculationAlwaysDelay
) {
161 BackoffEntry::Policy always_delay_policy
= base_policy
;
162 always_delay_policy
.always_use_initial_delay
= true;
163 always_delay_policy
.num_errors_to_ignore
= 2;
165 TestTickClock now_ticks
;
166 BackoffEntry
entry(&always_delay_policy
, &now_ticks
);
168 // With previous requests, should return "now".
169 TimeTicks result
= entry
.GetReleaseTime();
170 EXPECT_EQ(TimeDelta(), entry
.GetTimeUntilRelease());
173 entry
.InformOfRequest(false);
174 EXPECT_EQ(TimeDelta::FromMilliseconds(1000), entry
.GetTimeUntilRelease());
177 entry
.InformOfRequest(false);
178 EXPECT_EQ(TimeDelta::FromMilliseconds(1000), entry
.GetTimeUntilRelease());
180 // 3 errors, exponential backoff starts.
181 entry
.InformOfRequest(false);
182 EXPECT_EQ(TimeDelta::FromMilliseconds(2000), entry
.GetTimeUntilRelease());
185 entry
.InformOfRequest(false);
186 EXPECT_EQ(TimeDelta::FromMilliseconds(4000), entry
.GetTimeUntilRelease());
188 // 8 errors (to check it doesn't pass maximum).
189 entry
.InformOfRequest(false);
190 entry
.InformOfRequest(false);
191 entry
.InformOfRequest(false);
192 entry
.InformOfRequest(false);
193 result
= entry
.GetReleaseTime();
194 EXPECT_EQ(TimeDelta::FromMilliseconds(20000), entry
.GetTimeUntilRelease());
197 TEST(BackoffEntryTest
, ReleaseTimeCalculationWithJitter
) {
198 for (int i
= 0; i
< 10; ++i
) {
199 BackoffEntry::Policy jittery_policy
= base_policy
;
200 jittery_policy
.jitter_factor
= 0.2;
202 TestTickClock now_ticks
;
203 BackoffEntry
entry(&jittery_policy
, &now_ticks
);
205 entry
.InformOfRequest(false);
206 entry
.InformOfRequest(false);
207 entry
.InformOfRequest(false);
208 TimeTicks result
= entry
.GetReleaseTime();
209 EXPECT_LE(now_ticks
.NowTicks() + TimeDelta::FromMilliseconds(3200), result
);
210 EXPECT_GE(now_ticks
.NowTicks() + TimeDelta::FromMilliseconds(4000), result
);
214 TEST(BackoffEntryTest
, FailureThenSuccess
) {
215 TestTickClock now_ticks
;
216 BackoffEntry
entry(&base_policy
, &now_ticks
);
218 // Failure count 1, establishes horizon.
219 entry
.InformOfRequest(false);
220 TimeTicks release_time
= entry
.GetReleaseTime();
221 EXPECT_EQ(TimeTicks() + TimeDelta::FromMilliseconds(1000), release_time
);
223 // Success, failure count 0, should not advance past
224 // the horizon that was already set.
225 now_ticks
.set_now(release_time
- TimeDelta::FromMilliseconds(200));
226 entry
.InformOfRequest(true);
227 EXPECT_EQ(release_time
, entry
.GetReleaseTime());
229 // Failure, failure count 1.
230 entry
.InformOfRequest(false);
231 EXPECT_EQ(release_time
+ TimeDelta::FromMilliseconds(800),
232 entry
.GetReleaseTime());
235 TEST(BackoffEntryTest
, FailureThenSuccessAlwaysDelay
) {
236 BackoffEntry::Policy always_delay_policy
= base_policy
;
237 always_delay_policy
.always_use_initial_delay
= true;
238 always_delay_policy
.num_errors_to_ignore
= 1;
240 TestTickClock now_ticks
;
241 BackoffEntry
entry(&always_delay_policy
, &now_ticks
);
244 entry
.InformOfRequest(false);
245 EXPECT_EQ(TimeDelta::FromMilliseconds(1000), entry
.GetTimeUntilRelease());
248 entry
.InformOfRequest(false);
249 EXPECT_EQ(TimeDelta::FromMilliseconds(2000), entry
.GetTimeUntilRelease());
250 now_ticks
.set_now(entry
.GetReleaseTime() + TimeDelta::FromMilliseconds(2000));
252 // Success. We should go back to the original delay.
253 entry
.InformOfRequest(true);
254 EXPECT_EQ(TimeDelta::FromMilliseconds(1000), entry
.GetTimeUntilRelease());
256 // Failure count reaches 2 again. We should increase the delay once more.
257 entry
.InformOfRequest(false);
258 EXPECT_EQ(TimeDelta::FromMilliseconds(2000), entry
.GetTimeUntilRelease());
259 now_ticks
.set_now(entry
.GetReleaseTime() + TimeDelta::FromMilliseconds(2000));
262 TEST(BackoffEntryTest
, RetainCustomHorizon
) {
263 TestTickClock now_ticks
;
264 BackoffEntry
custom(&base_policy
, &now_ticks
);
265 TimeTicks custom_horizon
= TimeTicks() + TimeDelta::FromDays(3);
266 custom
.SetCustomReleaseTime(custom_horizon
);
267 custom
.InformOfRequest(false);
268 custom
.InformOfRequest(true);
269 now_ticks
.set_now(TimeTicks() + TimeDelta::FromDays(2));
270 custom
.InformOfRequest(false);
271 custom
.InformOfRequest(true);
272 EXPECT_EQ(custom_horizon
, custom
.GetReleaseTime());
274 // Now check that once we are at or past the custom horizon,
275 // we get normal behavior.
276 now_ticks
.set_now(TimeTicks() + TimeDelta::FromDays(3));
277 custom
.InformOfRequest(false);
279 TimeTicks() + TimeDelta::FromDays(3) + TimeDelta::FromMilliseconds(1000),
280 custom
.GetReleaseTime());
283 TEST(BackoffEntryTest
, RetainCustomHorizonWhenInitialErrorsIgnored
) {
284 // Regression test for a bug discovered during code review.
285 BackoffEntry::Policy lenient_policy
= base_policy
;
286 lenient_policy
.num_errors_to_ignore
= 1;
287 TestTickClock now_ticks
;
288 BackoffEntry
custom(&lenient_policy
, &now_ticks
);
289 TimeTicks custom_horizon
= TimeTicks() + TimeDelta::FromDays(3);
290 custom
.SetCustomReleaseTime(custom_horizon
);
291 custom
.InformOfRequest(false); // This must not reset the horizon.
292 EXPECT_EQ(custom_horizon
, custom
.GetReleaseTime());
295 TEST(BackoffEntryTest
, OverflowProtection
) {
296 BackoffEntry::Policy large_multiply_policy
= base_policy
;
297 large_multiply_policy
.multiply_factor
= 256;
298 TestTickClock now_ticks
;
299 BackoffEntry
custom(&large_multiply_policy
, &now_ticks
);
301 // Trigger enough failures such that more than 11 bits of exponent are used
302 // to represent the exponential backoff intermediate values. Given a multiply
303 // factor of 256 (2^8), 129 iterations is enough: 2^(8*(129-1)) = 2^1024.
304 for (int i
= 0; i
< 129; ++i
) {
305 now_ticks
.set_now(now_ticks
.NowTicks() + custom
.GetTimeUntilRelease());
306 custom
.InformOfRequest(false);
307 ASSERT_TRUE(custom
.ShouldRejectRequest());
310 // Max delay should still be respected.
311 EXPECT_EQ(20000, custom
.GetTimeUntilRelease().InMilliseconds());