1 // Copyright (c) 2011 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 "net/base/backoff_entry.h"
6 #include "testing/gtest/include/gtest/gtest.h"
7
8 namespace {
9
10 using base::TimeDelta;
11 using base::TimeTicks;
12 using net::BackoffEntry;
13
14 BackoffEntry::Policy base_policy = { 0, 1000, 2.0, 0.0, 20000, 2000 };
15
16 class TestBackoffEntry : public BackoffEntry {
17 public:
TestBackoffEntry(const Policy * const policy)18 explicit TestBackoffEntry(const Policy* const policy)
19 : BackoffEntry(policy),
20 now_(TimeTicks()) {
21 // Work around initialization in constructor not picking up
22 // fake time.
23 SetCustomReleaseTime(TimeTicks());
24 }
25
~TestBackoffEntry()26 virtual ~TestBackoffEntry() {}
27
GetTimeNow() const28 virtual TimeTicks GetTimeNow() const {
29 return now_;
30 }
31
set_now(const TimeTicks & now)32 void set_now(const TimeTicks& now) {
33 now_ = now;
34 }
35
36 private:
37 TimeTicks now_;
38
39 DISALLOW_COPY_AND_ASSIGN(TestBackoffEntry);
40 };
41
TEST(BackoffEntryTest,BaseTest)42 TEST(BackoffEntryTest, BaseTest) {
43 TestBackoffEntry entry(&base_policy);
44 EXPECT_FALSE(entry.ShouldRejectRequest());
45
46 entry.InformOfRequest(false);
47 EXPECT_TRUE(entry.ShouldRejectRequest());
48 }
49
TEST(BackoffEntryTest,CanDiscardNeverExpires)50 TEST(BackoffEntryTest, CanDiscardNeverExpires) {
51 BackoffEntry::Policy never_expires_policy = base_policy;
52 never_expires_policy.entry_lifetime_ms = -1;
53 TestBackoffEntry never_expires(&never_expires_policy);
54 EXPECT_FALSE(never_expires.CanDiscard());
55 never_expires.set_now(TimeTicks() + TimeDelta::FromDays(100));
56 EXPECT_FALSE(never_expires.CanDiscard());
57 }
58
TEST(BackoffEntryTest,CanDiscard)59 TEST(BackoffEntryTest, CanDiscard) {
60 TestBackoffEntry entry(&base_policy);
61 // Because lifetime is non-zero, we shouldn't be able to discard yet.
62 EXPECT_FALSE(entry.CanDiscard());
63
64 // Test the "being used" case.
65 entry.InformOfRequest(false);
66 EXPECT_FALSE(entry.CanDiscard());
67
68 // Test the case where there are errors but we can time out.
69 entry.set_now(
70 entry.GetReleaseTime() + TimeDelta::FromMilliseconds(1));
71 EXPECT_FALSE(entry.CanDiscard());
72 entry.set_now(entry.GetReleaseTime() + TimeDelta::FromMilliseconds(
73 base_policy.maximum_backoff_ms + 1));
74 EXPECT_TRUE(entry.CanDiscard());
75
76 // Test the final case (no errors, dependent only on specified lifetime).
77 entry.set_now(entry.GetReleaseTime() + TimeDelta::FromMilliseconds(
78 base_policy.entry_lifetime_ms - 1));
79 entry.InformOfRequest(true);
80 EXPECT_FALSE(entry.CanDiscard());
81 entry.set_now(entry.GetReleaseTime() + TimeDelta::FromMilliseconds(
82 base_policy.entry_lifetime_ms));
83 EXPECT_TRUE(entry.CanDiscard());
84 }
85
TEST(BackoffEntryTest,CanDiscardNotStored)86 TEST(BackoffEntryTest, CanDiscardNotStored) {
87 BackoffEntry::Policy no_store_policy = base_policy;
88 no_store_policy.entry_lifetime_ms = 0;
89 TestBackoffEntry not_stored(&no_store_policy);
90 EXPECT_TRUE(not_stored.CanDiscard());
91 }
92
TEST(BackoffEntryTest,ShouldIgnoreFirstTwo)93 TEST(BackoffEntryTest, ShouldIgnoreFirstTwo) {
94 BackoffEntry::Policy lenient_policy = base_policy;
95 lenient_policy.num_errors_to_ignore = 2;
96
97 BackoffEntry entry(&lenient_policy);
98 entry.InformOfRequest(false);
99 EXPECT_FALSE(entry.ShouldRejectRequest());
100 entry.InformOfRequest(false);
101 EXPECT_FALSE(entry.ShouldRejectRequest());
102 entry.InformOfRequest(false);
103 EXPECT_TRUE(entry.ShouldRejectRequest());
104 }
105
TEST(BackoffEntryTest,ReleaseTimeCalculation)106 TEST(BackoffEntryTest, ReleaseTimeCalculation) {
107 TestBackoffEntry entry(&base_policy);
108
109 // With zero errors, should return "now".
110 TimeTicks result = entry.GetReleaseTime();
111 EXPECT_EQ(entry.GetTimeNow(), result);
112
113 // 1 error.
114 entry.InformOfRequest(false);
115 result = entry.GetReleaseTime();
116 EXPECT_EQ(entry.GetTimeNow() + TimeDelta::FromMilliseconds(1000), result);
117
118 // 2 errors.
119 entry.InformOfRequest(false);
120 result = entry.GetReleaseTime();
121 EXPECT_EQ(entry.GetTimeNow() + TimeDelta::FromMilliseconds(2000), result);
122
123 // 3 errors.
124 entry.InformOfRequest(false);
125 result = entry.GetReleaseTime();
126 EXPECT_EQ(entry.GetTimeNow() + TimeDelta::FromMilliseconds(4000), result);
127
128 // 6 errors (to check it doesn't pass maximum).
129 entry.InformOfRequest(false);
130 entry.InformOfRequest(false);
131 entry.InformOfRequest(false);
132 result = entry.GetReleaseTime();
133 EXPECT_EQ(entry.GetTimeNow() + TimeDelta::FromMilliseconds(20000), result);
134 }
135
TEST(BackoffEntryTest,ReleaseTimeCalculationWithJitter)136 TEST(BackoffEntryTest, ReleaseTimeCalculationWithJitter) {
137 for (int i = 0; i < 10; ++i) {
138 BackoffEntry::Policy jittery_policy = base_policy;
139 jittery_policy.jitter_factor = 0.2;
140
141 TestBackoffEntry entry(&jittery_policy);
142
143 entry.InformOfRequest(false);
144 entry.InformOfRequest(false);
145 entry.InformOfRequest(false);
146 TimeTicks result = entry.GetReleaseTime();
147 EXPECT_LE(entry.GetTimeNow() + TimeDelta::FromMilliseconds(3200), result);
148 EXPECT_GE(entry.GetTimeNow() + TimeDelta::FromMilliseconds(4000), result);
149 }
150 }
151
TEST(BackoffEntryTest,FailureThenSuccess)152 TEST(BackoffEntryTest, FailureThenSuccess) {
153 TestBackoffEntry entry(&base_policy);
154
155 // Failure count 1, establishes horizon.
156 entry.InformOfRequest(false);
157 TimeTicks release_time = entry.GetReleaseTime();
158 EXPECT_EQ(TimeTicks() + TimeDelta::FromMilliseconds(1000), release_time);
159
160 // Success, failure count 0, should not advance past
161 // the horizon that was already set.
162 entry.set_now(release_time - TimeDelta::FromMilliseconds(200));
163 entry.InformOfRequest(true);
164 EXPECT_EQ(release_time, entry.GetReleaseTime());
165
166 // Failure, failure count 1.
167 entry.InformOfRequest(false);
168 EXPECT_EQ(release_time + TimeDelta::FromMilliseconds(800),
169 entry.GetReleaseTime());
170 }
171
TEST(BackoffEntryTest,RetainCustomHorizon)172 TEST(BackoffEntryTest, RetainCustomHorizon) {
173 TestBackoffEntry custom(&base_policy);
174 TimeTicks custom_horizon = TimeTicks() + TimeDelta::FromDays(3);
175 custom.SetCustomReleaseTime(custom_horizon);
176 custom.InformOfRequest(false);
177 custom.InformOfRequest(true);
178 custom.set_now(TimeTicks() + TimeDelta::FromDays(2));
179 custom.InformOfRequest(false);
180 custom.InformOfRequest(true);
181 EXPECT_EQ(custom_horizon, custom.GetReleaseTime());
182
183 // Now check that once we are at or past the custom horizon,
184 // we get normal behavior.
185 custom.set_now(TimeTicks() + TimeDelta::FromDays(3));
186 custom.InformOfRequest(false);
187 EXPECT_EQ(
188 TimeTicks() + TimeDelta::FromDays(3) + TimeDelta::FromMilliseconds(1000),
189 custom.GetReleaseTime());
190 }
191
TEST(BackoffEntryTest,RetainCustomHorizonWhenInitialErrorsIgnored)192 TEST(BackoffEntryTest, RetainCustomHorizonWhenInitialErrorsIgnored) {
193 // Regression test for a bug discovered during code review.
194 BackoffEntry::Policy lenient_policy = base_policy;
195 lenient_policy.num_errors_to_ignore = 1;
196 TestBackoffEntry custom(&lenient_policy);
197 TimeTicks custom_horizon = TimeTicks() + TimeDelta::FromDays(3);
198 custom.SetCustomReleaseTime(custom_horizon);
199 custom.InformOfRequest(false); // This must not reset the horizon.
200 EXPECT_EQ(custom_horizon, custom.GetReleaseTime());
201 }
202
203 } // namespace
204