xref: /aosp_15_r20/external/libbrillo/brillo/backoff_entry_test.cc (revision 1a96fba65179ea7d3f56207137718607415c5953)
1*1a96fba6SXin Li // Copyright 2015 The Chromium OS Authors. All rights reserved.
2*1a96fba6SXin Li // Use of this source code is governed by a BSD-style license that can be
3*1a96fba6SXin Li // found in the LICENSE file.
4*1a96fba6SXin Li 
5*1a96fba6SXin Li #include <brillo/backoff_entry.h>
6*1a96fba6SXin Li #include <gtest/gtest.h>
7*1a96fba6SXin Li 
8*1a96fba6SXin Li using base::TimeDelta;
9*1a96fba6SXin Li using base::TimeTicks;
10*1a96fba6SXin Li 
11*1a96fba6SXin Li namespace brillo {
12*1a96fba6SXin Li 
13*1a96fba6SXin Li BackoffEntry::Policy base_policy = { 0, 1000, 2.0, 0.0, 20000, 2000, false };
14*1a96fba6SXin Li 
15*1a96fba6SXin Li class TestBackoffEntry : public BackoffEntry {
16*1a96fba6SXin Li  public:
TestBackoffEntry(const Policy * const policy)17*1a96fba6SXin Li   explicit TestBackoffEntry(const Policy* const policy)
18*1a96fba6SXin Li       : BackoffEntry(policy),
19*1a96fba6SXin Li         now_(TimeTicks()) {
20*1a96fba6SXin Li     // Work around initialization in constructor not picking up
21*1a96fba6SXin Li     // fake time.
22*1a96fba6SXin Li     SetCustomReleaseTime(TimeTicks());
23*1a96fba6SXin Li   }
24*1a96fba6SXin Li 
~TestBackoffEntry()25*1a96fba6SXin Li   ~TestBackoffEntry() override {}
26*1a96fba6SXin Li 
ImplGetTimeNow() const27*1a96fba6SXin Li   TimeTicks ImplGetTimeNow() const override { return now_; }
28*1a96fba6SXin Li 
set_now(const TimeTicks & now)29*1a96fba6SXin Li   void set_now(const TimeTicks& now) {
30*1a96fba6SXin Li     now_ = now;
31*1a96fba6SXin Li   }
32*1a96fba6SXin Li 
33*1a96fba6SXin Li  private:
34*1a96fba6SXin Li   TimeTicks now_;
35*1a96fba6SXin Li 
36*1a96fba6SXin Li   DISALLOW_COPY_AND_ASSIGN(TestBackoffEntry);
37*1a96fba6SXin Li };
38*1a96fba6SXin Li 
TEST(BackoffEntryTest,BaseTest)39*1a96fba6SXin Li TEST(BackoffEntryTest, BaseTest) {
40*1a96fba6SXin Li   TestBackoffEntry entry(&base_policy);
41*1a96fba6SXin Li   EXPECT_FALSE(entry.ShouldRejectRequest());
42*1a96fba6SXin Li   EXPECT_EQ(TimeDelta(), entry.GetTimeUntilRelease());
43*1a96fba6SXin Li 
44*1a96fba6SXin Li   entry.InformOfRequest(false);
45*1a96fba6SXin Li   EXPECT_TRUE(entry.ShouldRejectRequest());
46*1a96fba6SXin Li   EXPECT_EQ(TimeDelta::FromMilliseconds(1000), entry.GetTimeUntilRelease());
47*1a96fba6SXin Li }
48*1a96fba6SXin Li 
TEST(BackoffEntryTest,CanDiscardNeverExpires)49*1a96fba6SXin Li TEST(BackoffEntryTest, CanDiscardNeverExpires) {
50*1a96fba6SXin Li   BackoffEntry::Policy never_expires_policy = base_policy;
51*1a96fba6SXin Li   never_expires_policy.entry_lifetime_ms = -1;
52*1a96fba6SXin Li   TestBackoffEntry never_expires(&never_expires_policy);
53*1a96fba6SXin Li   EXPECT_FALSE(never_expires.CanDiscard());
54*1a96fba6SXin Li   never_expires.set_now(TimeTicks() + TimeDelta::FromDays(100));
55*1a96fba6SXin Li   EXPECT_FALSE(never_expires.CanDiscard());
56*1a96fba6SXin Li }
57*1a96fba6SXin Li 
TEST(BackoffEntryTest,CanDiscard)58*1a96fba6SXin Li TEST(BackoffEntryTest, CanDiscard) {
59*1a96fba6SXin Li   TestBackoffEntry entry(&base_policy);
60*1a96fba6SXin Li   // Because lifetime is non-zero, we shouldn't be able to discard yet.
61*1a96fba6SXin Li   EXPECT_FALSE(entry.CanDiscard());
62*1a96fba6SXin Li 
63*1a96fba6SXin Li   // Test the "being used" case.
64*1a96fba6SXin Li   entry.InformOfRequest(false);
65*1a96fba6SXin Li   EXPECT_FALSE(entry.CanDiscard());
66*1a96fba6SXin Li 
67*1a96fba6SXin Li   // Test the case where there are errors but we can time out.
68*1a96fba6SXin Li   entry.set_now(
69*1a96fba6SXin Li       entry.GetReleaseTime() + TimeDelta::FromMilliseconds(1));
70*1a96fba6SXin Li   EXPECT_FALSE(entry.CanDiscard());
71*1a96fba6SXin Li   entry.set_now(entry.GetReleaseTime() + TimeDelta::FromMilliseconds(
72*1a96fba6SXin Li       base_policy.maximum_backoff_ms + 1));
73*1a96fba6SXin Li   EXPECT_TRUE(entry.CanDiscard());
74*1a96fba6SXin Li 
75*1a96fba6SXin Li   // Test the final case (no errors, dependent only on specified lifetime).
76*1a96fba6SXin Li   entry.set_now(entry.GetReleaseTime() + TimeDelta::FromMilliseconds(
77*1a96fba6SXin Li       base_policy.entry_lifetime_ms - 1));
78*1a96fba6SXin Li   entry.InformOfRequest(true);
79*1a96fba6SXin Li   EXPECT_FALSE(entry.CanDiscard());
80*1a96fba6SXin Li   entry.set_now(entry.GetReleaseTime() + TimeDelta::FromMilliseconds(
81*1a96fba6SXin Li       base_policy.entry_lifetime_ms));
82*1a96fba6SXin Li   EXPECT_TRUE(entry.CanDiscard());
83*1a96fba6SXin Li }
84*1a96fba6SXin Li 
TEST(BackoffEntryTest,CanDiscardAlwaysDelay)85*1a96fba6SXin Li TEST(BackoffEntryTest, CanDiscardAlwaysDelay) {
86*1a96fba6SXin Li   BackoffEntry::Policy always_delay_policy = base_policy;
87*1a96fba6SXin Li   always_delay_policy.always_use_initial_delay = true;
88*1a96fba6SXin Li   always_delay_policy.entry_lifetime_ms = 0;
89*1a96fba6SXin Li 
90*1a96fba6SXin Li   TestBackoffEntry entry(&always_delay_policy);
91*1a96fba6SXin Li 
92*1a96fba6SXin Li   // Because lifetime is non-zero, we shouldn't be able to discard yet.
93*1a96fba6SXin Li   entry.set_now(entry.GetReleaseTime() + TimeDelta::FromMilliseconds(2000));
94*1a96fba6SXin Li   EXPECT_TRUE(entry.CanDiscard());
95*1a96fba6SXin Li 
96*1a96fba6SXin Li   // Even with no failures, we wait until the delay before we allow discard.
97*1a96fba6SXin Li   entry.InformOfRequest(true);
98*1a96fba6SXin Li   EXPECT_FALSE(entry.CanDiscard());
99*1a96fba6SXin Li 
100*1a96fba6SXin Li   // Wait until the delay expires, and we can discard the entry again.
101*1a96fba6SXin Li   entry.set_now(entry.GetReleaseTime() + TimeDelta::FromMilliseconds(1000));
102*1a96fba6SXin Li   EXPECT_TRUE(entry.CanDiscard());
103*1a96fba6SXin Li }
104*1a96fba6SXin Li 
TEST(BackoffEntryTest,CanDiscardNotStored)105*1a96fba6SXin Li TEST(BackoffEntryTest, CanDiscardNotStored) {
106*1a96fba6SXin Li   BackoffEntry::Policy no_store_policy = base_policy;
107*1a96fba6SXin Li   no_store_policy.entry_lifetime_ms = 0;
108*1a96fba6SXin Li   TestBackoffEntry not_stored(&no_store_policy);
109*1a96fba6SXin Li   EXPECT_TRUE(not_stored.CanDiscard());
110*1a96fba6SXin Li }
111*1a96fba6SXin Li 
TEST(BackoffEntryTest,ShouldIgnoreFirstTwo)112*1a96fba6SXin Li TEST(BackoffEntryTest, ShouldIgnoreFirstTwo) {
113*1a96fba6SXin Li   BackoffEntry::Policy lenient_policy = base_policy;
114*1a96fba6SXin Li   lenient_policy.num_errors_to_ignore = 2;
115*1a96fba6SXin Li 
116*1a96fba6SXin Li   BackoffEntry entry(&lenient_policy);
117*1a96fba6SXin Li 
118*1a96fba6SXin Li   entry.InformOfRequest(false);
119*1a96fba6SXin Li   EXPECT_FALSE(entry.ShouldRejectRequest());
120*1a96fba6SXin Li 
121*1a96fba6SXin Li   entry.InformOfRequest(false);
122*1a96fba6SXin Li   EXPECT_FALSE(entry.ShouldRejectRequest());
123*1a96fba6SXin Li 
124*1a96fba6SXin Li   entry.InformOfRequest(false);
125*1a96fba6SXin Li   EXPECT_TRUE(entry.ShouldRejectRequest());
126*1a96fba6SXin Li }
127*1a96fba6SXin Li 
TEST(BackoffEntryTest,ReleaseTimeCalculation)128*1a96fba6SXin Li TEST(BackoffEntryTest, ReleaseTimeCalculation) {
129*1a96fba6SXin Li   TestBackoffEntry entry(&base_policy);
130*1a96fba6SXin Li 
131*1a96fba6SXin Li   // With zero errors, should return "now".
132*1a96fba6SXin Li   TimeTicks result = entry.GetReleaseTime();
133*1a96fba6SXin Li   EXPECT_EQ(entry.ImplGetTimeNow(), result);
134*1a96fba6SXin Li 
135*1a96fba6SXin Li   // 1 error.
136*1a96fba6SXin Li   entry.InformOfRequest(false);
137*1a96fba6SXin Li   result = entry.GetReleaseTime();
138*1a96fba6SXin Li   EXPECT_EQ(entry.ImplGetTimeNow() + TimeDelta::FromMilliseconds(1000), result);
139*1a96fba6SXin Li   EXPECT_EQ(TimeDelta::FromMilliseconds(1000), entry.GetTimeUntilRelease());
140*1a96fba6SXin Li 
141*1a96fba6SXin Li   // 2 errors.
142*1a96fba6SXin Li   entry.InformOfRequest(false);
143*1a96fba6SXin Li   result = entry.GetReleaseTime();
144*1a96fba6SXin Li   EXPECT_EQ(entry.ImplGetTimeNow() + TimeDelta::FromMilliseconds(2000), result);
145*1a96fba6SXin Li   EXPECT_EQ(TimeDelta::FromMilliseconds(2000), entry.GetTimeUntilRelease());
146*1a96fba6SXin Li 
147*1a96fba6SXin Li   // 3 errors.
148*1a96fba6SXin Li   entry.InformOfRequest(false);
149*1a96fba6SXin Li   result = entry.GetReleaseTime();
150*1a96fba6SXin Li   EXPECT_EQ(entry.ImplGetTimeNow() + TimeDelta::FromMilliseconds(4000), result);
151*1a96fba6SXin Li   EXPECT_EQ(TimeDelta::FromMilliseconds(4000), entry.GetTimeUntilRelease());
152*1a96fba6SXin Li 
153*1a96fba6SXin Li   // 6 errors (to check it doesn't pass maximum).
154*1a96fba6SXin Li   entry.InformOfRequest(false);
155*1a96fba6SXin Li   entry.InformOfRequest(false);
156*1a96fba6SXin Li   entry.InformOfRequest(false);
157*1a96fba6SXin Li   result = entry.GetReleaseTime();
158*1a96fba6SXin Li   EXPECT_EQ(
159*1a96fba6SXin Li       entry.ImplGetTimeNow() + TimeDelta::FromMilliseconds(20000), result);
160*1a96fba6SXin Li }
161*1a96fba6SXin Li 
TEST(BackoffEntryTest,ReleaseTimeCalculationAlwaysDelay)162*1a96fba6SXin Li TEST(BackoffEntryTest, ReleaseTimeCalculationAlwaysDelay) {
163*1a96fba6SXin Li   BackoffEntry::Policy always_delay_policy = base_policy;
164*1a96fba6SXin Li   always_delay_policy.always_use_initial_delay = true;
165*1a96fba6SXin Li   always_delay_policy.num_errors_to_ignore = 2;
166*1a96fba6SXin Li 
167*1a96fba6SXin Li   TestBackoffEntry entry(&always_delay_policy);
168*1a96fba6SXin Li 
169*1a96fba6SXin Li   // With previous requests, should return "now".
170*1a96fba6SXin Li   TimeTicks result = entry.GetReleaseTime();
171*1a96fba6SXin Li   EXPECT_EQ(TimeDelta(), entry.GetTimeUntilRelease());
172*1a96fba6SXin Li 
173*1a96fba6SXin Li   // 1 error.
174*1a96fba6SXin Li   entry.InformOfRequest(false);
175*1a96fba6SXin Li   EXPECT_EQ(TimeDelta::FromMilliseconds(1000), entry.GetTimeUntilRelease());
176*1a96fba6SXin Li 
177*1a96fba6SXin Li   // 2 errors.
178*1a96fba6SXin Li   entry.InformOfRequest(false);
179*1a96fba6SXin Li   EXPECT_EQ(TimeDelta::FromMilliseconds(1000), entry.GetTimeUntilRelease());
180*1a96fba6SXin Li 
181*1a96fba6SXin Li   // 3 errors, exponential backoff starts.
182*1a96fba6SXin Li   entry.InformOfRequest(false);
183*1a96fba6SXin Li   EXPECT_EQ(TimeDelta::FromMilliseconds(2000), entry.GetTimeUntilRelease());
184*1a96fba6SXin Li 
185*1a96fba6SXin Li   // 4 errors.
186*1a96fba6SXin Li   entry.InformOfRequest(false);
187*1a96fba6SXin Li   EXPECT_EQ(TimeDelta::FromMilliseconds(4000), entry.GetTimeUntilRelease());
188*1a96fba6SXin Li 
189*1a96fba6SXin Li   // 8 errors (to check it doesn't pass maximum).
190*1a96fba6SXin Li   entry.InformOfRequest(false);
191*1a96fba6SXin Li   entry.InformOfRequest(false);
192*1a96fba6SXin Li   entry.InformOfRequest(false);
193*1a96fba6SXin Li   entry.InformOfRequest(false);
194*1a96fba6SXin Li   result = entry.GetReleaseTime();
195*1a96fba6SXin Li   EXPECT_EQ(TimeDelta::FromMilliseconds(20000), entry.GetTimeUntilRelease());
196*1a96fba6SXin Li }
197*1a96fba6SXin Li 
TEST(BackoffEntryTest,ReleaseTimeCalculationWithJitter)198*1a96fba6SXin Li TEST(BackoffEntryTest, ReleaseTimeCalculationWithJitter) {
199*1a96fba6SXin Li   for (int i = 0; i < 10; ++i) {
200*1a96fba6SXin Li     BackoffEntry::Policy jittery_policy = base_policy;
201*1a96fba6SXin Li     jittery_policy.jitter_factor = 0.2;
202*1a96fba6SXin Li 
203*1a96fba6SXin Li     TestBackoffEntry entry(&jittery_policy);
204*1a96fba6SXin Li 
205*1a96fba6SXin Li     entry.InformOfRequest(false);
206*1a96fba6SXin Li     entry.InformOfRequest(false);
207*1a96fba6SXin Li     entry.InformOfRequest(false);
208*1a96fba6SXin Li     TimeTicks result = entry.GetReleaseTime();
209*1a96fba6SXin Li     EXPECT_LE(
210*1a96fba6SXin Li         entry.ImplGetTimeNow() + TimeDelta::FromMilliseconds(3200), result);
211*1a96fba6SXin Li     EXPECT_GE(
212*1a96fba6SXin Li         entry.ImplGetTimeNow() + TimeDelta::FromMilliseconds(4000), result);
213*1a96fba6SXin Li   }
214*1a96fba6SXin Li }
215*1a96fba6SXin Li 
TEST(BackoffEntryTest,FailureThenSuccess)216*1a96fba6SXin Li TEST(BackoffEntryTest, FailureThenSuccess) {
217*1a96fba6SXin Li   TestBackoffEntry entry(&base_policy);
218*1a96fba6SXin Li 
219*1a96fba6SXin Li   // Failure count 1, establishes horizon.
220*1a96fba6SXin Li   entry.InformOfRequest(false);
221*1a96fba6SXin Li   TimeTicks release_time = entry.GetReleaseTime();
222*1a96fba6SXin Li   EXPECT_EQ(TimeTicks() + TimeDelta::FromMilliseconds(1000), release_time);
223*1a96fba6SXin Li 
224*1a96fba6SXin Li   // Success, failure count 0, should not advance past
225*1a96fba6SXin Li   // the horizon that was already set.
226*1a96fba6SXin Li   entry.set_now(release_time - TimeDelta::FromMilliseconds(200));
227*1a96fba6SXin Li   entry.InformOfRequest(true);
228*1a96fba6SXin Li   EXPECT_EQ(release_time, entry.GetReleaseTime());
229*1a96fba6SXin Li 
230*1a96fba6SXin Li   // Failure, failure count 1.
231*1a96fba6SXin Li   entry.InformOfRequest(false);
232*1a96fba6SXin Li   EXPECT_EQ(release_time + TimeDelta::FromMilliseconds(800),
233*1a96fba6SXin Li             entry.GetReleaseTime());
234*1a96fba6SXin Li }
235*1a96fba6SXin Li 
TEST(BackoffEntryTest,FailureThenSuccessAlwaysDelay)236*1a96fba6SXin Li TEST(BackoffEntryTest, FailureThenSuccessAlwaysDelay) {
237*1a96fba6SXin Li   BackoffEntry::Policy always_delay_policy = base_policy;
238*1a96fba6SXin Li   always_delay_policy.always_use_initial_delay = true;
239*1a96fba6SXin Li   always_delay_policy.num_errors_to_ignore = 1;
240*1a96fba6SXin Li 
241*1a96fba6SXin Li   TestBackoffEntry entry(&always_delay_policy);
242*1a96fba6SXin Li 
243*1a96fba6SXin Li   // Failure count 1.
244*1a96fba6SXin Li   entry.InformOfRequest(false);
245*1a96fba6SXin Li   EXPECT_EQ(TimeDelta::FromMilliseconds(1000), entry.GetTimeUntilRelease());
246*1a96fba6SXin Li 
247*1a96fba6SXin Li   // Failure count 2.
248*1a96fba6SXin Li   entry.InformOfRequest(false);
249*1a96fba6SXin Li   EXPECT_EQ(TimeDelta::FromMilliseconds(2000), entry.GetTimeUntilRelease());
250*1a96fba6SXin Li   entry.set_now(entry.GetReleaseTime() + TimeDelta::FromMilliseconds(2000));
251*1a96fba6SXin Li 
252*1a96fba6SXin Li   // Success.  We should go back to the original delay.
253*1a96fba6SXin Li   entry.InformOfRequest(true);
254*1a96fba6SXin Li   EXPECT_EQ(TimeDelta::FromMilliseconds(1000), entry.GetTimeUntilRelease());
255*1a96fba6SXin Li 
256*1a96fba6SXin Li   // Failure count reaches 2 again.  We should increase the delay once more.
257*1a96fba6SXin Li   entry.InformOfRequest(false);
258*1a96fba6SXin Li   EXPECT_EQ(TimeDelta::FromMilliseconds(2000), entry.GetTimeUntilRelease());
259*1a96fba6SXin Li   entry.set_now(entry.GetReleaseTime() + TimeDelta::FromMilliseconds(2000));
260*1a96fba6SXin Li }
261*1a96fba6SXin Li 
TEST(BackoffEntryTest,RetainCustomHorizon)262*1a96fba6SXin Li TEST(BackoffEntryTest, RetainCustomHorizon) {
263*1a96fba6SXin Li   TestBackoffEntry custom(&base_policy);
264*1a96fba6SXin Li   TimeTicks custom_horizon = TimeTicks() + TimeDelta::FromDays(3);
265*1a96fba6SXin Li   custom.SetCustomReleaseTime(custom_horizon);
266*1a96fba6SXin Li   custom.InformOfRequest(false);
267*1a96fba6SXin Li   custom.InformOfRequest(true);
268*1a96fba6SXin Li   custom.set_now(TimeTicks() + TimeDelta::FromDays(2));
269*1a96fba6SXin Li   custom.InformOfRequest(false);
270*1a96fba6SXin Li   custom.InformOfRequest(true);
271*1a96fba6SXin Li   EXPECT_EQ(custom_horizon, custom.GetReleaseTime());
272*1a96fba6SXin Li 
273*1a96fba6SXin Li   // Now check that once we are at or past the custom horizon,
274*1a96fba6SXin Li   // we get normal behavior.
275*1a96fba6SXin Li   custom.set_now(TimeTicks() + TimeDelta::FromDays(3));
276*1a96fba6SXin Li   custom.InformOfRequest(false);
277*1a96fba6SXin Li   EXPECT_EQ(
278*1a96fba6SXin Li       TimeTicks() + TimeDelta::FromDays(3) + TimeDelta::FromMilliseconds(1000),
279*1a96fba6SXin Li       custom.GetReleaseTime());
280*1a96fba6SXin Li }
281*1a96fba6SXin Li 
TEST(BackoffEntryTest,RetainCustomHorizonWhenInitialErrorsIgnored)282*1a96fba6SXin Li TEST(BackoffEntryTest, RetainCustomHorizonWhenInitialErrorsIgnored) {
283*1a96fba6SXin Li   // Regression test for a bug discovered during code review.
284*1a96fba6SXin Li   BackoffEntry::Policy lenient_policy = base_policy;
285*1a96fba6SXin Li   lenient_policy.num_errors_to_ignore = 1;
286*1a96fba6SXin Li   TestBackoffEntry custom(&lenient_policy);
287*1a96fba6SXin Li   TimeTicks custom_horizon = TimeTicks() + TimeDelta::FromDays(3);
288*1a96fba6SXin Li   custom.SetCustomReleaseTime(custom_horizon);
289*1a96fba6SXin Li   custom.InformOfRequest(false);  // This must not reset the horizon.
290*1a96fba6SXin Li   EXPECT_EQ(custom_horizon, custom.GetReleaseTime());
291*1a96fba6SXin Li }
292*1a96fba6SXin Li 
TEST(BackoffEntryTest,OverflowProtection)293*1a96fba6SXin Li TEST(BackoffEntryTest, OverflowProtection) {
294*1a96fba6SXin Li   BackoffEntry::Policy large_multiply_policy = base_policy;
295*1a96fba6SXin Li   large_multiply_policy.multiply_factor = 256;
296*1a96fba6SXin Li   TestBackoffEntry custom(&large_multiply_policy);
297*1a96fba6SXin Li 
298*1a96fba6SXin Li   // Trigger enough failures such that more than 11 bits of exponent are used
299*1a96fba6SXin Li   // to represent the exponential backoff intermediate values. Given a multiply
300*1a96fba6SXin Li   // factor of 256 (2^8), 129 iterations is enough: 2^(8*(129-1)) = 2^1024.
301*1a96fba6SXin Li   for (int i = 0; i < 129; ++i) {
302*1a96fba6SXin Li     custom.set_now(custom.ImplGetTimeNow() + custom.GetTimeUntilRelease());
303*1a96fba6SXin Li     custom.InformOfRequest(false);
304*1a96fba6SXin Li     ASSERT_TRUE(custom.ShouldRejectRequest());
305*1a96fba6SXin Li   }
306*1a96fba6SXin Li 
307*1a96fba6SXin Li   // Max delay should still be respected.
308*1a96fba6SXin Li   EXPECT_EQ(20000, custom.GetTimeUntilRelease().InMilliseconds());
309*1a96fba6SXin Li }
310*1a96fba6SXin Li 
311*1a96fba6SXin Li }  // namespace brillo
312