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
8namespace {
9
10using base::TimeDelta;
11using base::TimeTicks;
12using net::BackoffEntry;
13
14BackoffEntry::Policy base_policy = { 0, 1000, 2.0, 0.0, 20000, 2000 };
15
16class TestBackoffEntry : public BackoffEntry {
17 public:
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
26  virtual ~TestBackoffEntry() {}
27
28  virtual TimeTicks GetTimeNow() const {
29    return now_;
30  }
31
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
42TEST(BackoffEntryTest, BaseTest) {
43  TestBackoffEntry entry(&base_policy);
44  EXPECT_FALSE(entry.ShouldRejectRequest());
45
46  entry.InformOfRequest(false);
47  EXPECT_TRUE(entry.ShouldRejectRequest());
48}
49
50TEST(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
59TEST(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
86TEST(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
93TEST(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
106TEST(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
136TEST(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
152TEST(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
172TEST(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
192TEST(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