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