| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "net/base/backoff_entry.h" |
| |
| #include "base/time/tick_clock.h" |
| #include "base/values.h" |
| #include "net/base/backoff_entry_serializer.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace net { |
| |
| namespace { |
| |
| using base::Time; |
| using base::TimeDelta; |
| using base::TimeTicks; |
| |
| BackoffEntry::Policy base_policy = { |
| 0 /* num_errors_to_ignore */, |
| 1000 /* initial_delay_ms */, |
| 2.0 /* multiply_factor */, |
| 0.0 /* jitter_factor */, |
| 20000 /* maximum_backoff_ms */, |
| 2000 /* entry_lifetime_ms */, |
| false /* always_use_initial_delay */ |
| }; |
| |
| class TestTickClock : public base::TickClock { |
| public: |
| TestTickClock() = default; |
| TestTickClock(const TestTickClock&) = delete; |
| TestTickClock& operator=(const TestTickClock&) = delete; |
| ~TestTickClock() override = default; |
| |
| TimeTicks NowTicks() const override { return now_ticks_; } |
| void set_now(TimeTicks now) { now_ticks_ = now; } |
| |
| private: |
| TimeTicks now_ticks_; |
| }; |
| |
| // This test exercises the code that computes the "backoff duration" and tests |
| // BackoffEntrySerializer::SerializeToValue computes the backoff duration of a |
| // BackoffEntry by subtracting two base::TimeTicks values. Note that |
| // base::TimeTicks::operator- does not protect against overflow. Because |
| // SerializeToValue never returns null, its resolution strategy is to default to |
| // a zero base::TimeDelta when the subtraction would overflow. |
| TEST(BackoffEntrySerializerTest, SpecialCasesOfBackoffDuration) { |
| const base::TimeTicks kZeroTicks; |
| |
| struct TestCase { |
| base::TimeTicks release_time; |
| base::TimeTicks timeticks_now; |
| base::TimeDelta expected_backoff_duration; |
| }; |
| TestCase test_cases[] = { |
| // Non-overflowing subtraction works as expected. |
| { |
| .release_time = kZeroTicks + base::TimeDelta::FromMicroseconds(100), |
| .timeticks_now = kZeroTicks + base::TimeDelta::FromMicroseconds(75), |
| .expected_backoff_duration = base::TimeDelta::FromMicroseconds(25), |
| }, |
| { |
| .release_time = kZeroTicks + base::TimeDelta::FromMicroseconds(25), |
| .timeticks_now = kZeroTicks + base::TimeDelta::FromMicroseconds(100), |
| .expected_backoff_duration = base::TimeDelta::FromMicroseconds(-75), |
| }, |
| // Defaults to zero when one of the operands is +/- infinity. |
| { |
| .release_time = base::TimeTicks::Min(), |
| .timeticks_now = kZeroTicks, |
| .expected_backoff_duration = base::TimeDelta(), |
| }, |
| { |
| .release_time = base::TimeTicks::Max(), |
| .timeticks_now = kZeroTicks, |
| .expected_backoff_duration = base::TimeDelta(), |
| }, |
| { |
| .release_time = kZeroTicks, |
| .timeticks_now = base::TimeTicks::Min(), |
| .expected_backoff_duration = base::TimeDelta(), |
| }, |
| { |
| .release_time = kZeroTicks, |
| .timeticks_now = base::TimeTicks::Max(), |
| .expected_backoff_duration = base::TimeDelta(), |
| }, |
| // Defaults to zero when both of the operands are +/- infinity. |
| { |
| .release_time = base::TimeTicks::Min(), |
| .timeticks_now = base::TimeTicks::Min(), |
| .expected_backoff_duration = base::TimeDelta(), |
| }, |
| { |
| .release_time = base::TimeTicks::Min(), |
| .timeticks_now = base::TimeTicks::Max(), |
| .expected_backoff_duration = base::TimeDelta(), |
| }, |
| { |
| .release_time = base::TimeTicks::Max(), |
| .timeticks_now = base::TimeTicks::Min(), |
| .expected_backoff_duration = base::TimeDelta(), |
| }, |
| { |
| .release_time = base::TimeTicks::Max(), |
| .timeticks_now = base::TimeTicks::Max(), |
| .expected_backoff_duration = base::TimeDelta(), |
| }, |
| // Defaults to zero when the subtraction overflows, even when neither |
| // operand is infinity. |
| { |
| .release_time = |
| base::TimeTicks::Max() - base::TimeDelta::FromMicroseconds(1), |
| .timeticks_now = kZeroTicks + base::TimeDelta::FromMicroseconds(-1), |
| .expected_backoff_duration = base::TimeDelta(), |
| }, |
| // Defaults to zero when the computed backoff_duration cannot be |
| // represented as a double. |
| { |
| // Note that |release_time| must be finite. Otherwise, |
| // SerializeToValue will not compute |release_time - timeticks_now|. |
| .release_time = |
| base::TimeTicks::Max() - base::TimeDelta::FromMicroseconds(1), |
| .timeticks_now = kZeroTicks, |
| .expected_backoff_duration = base::TimeDelta(), |
| }, |
| }; |
| |
| for (const TestCase& test_case : test_cases) { |
| Time original_time = base::Time::Now(); |
| TestTickClock original_ticks; |
| original_ticks.set_now(test_case.timeticks_now); |
| BackoffEntry original(&base_policy, &original_ticks); |
| // Set the custom release time. |
| original.SetCustomReleaseTime(test_case.release_time); |
| std::unique_ptr<base::Value> serialized = |
| BackoffEntrySerializer::SerializeToValue(original, original_time); |
| |
| // Check that the serialized backoff duration matches our expectation. |
| double serialized_backoff_duration_double; |
| EXPECT_TRUE(serialized->GetList()[2].GetAsDouble( |
| &serialized_backoff_duration_double)); |
| base::TimeDelta serialized_backoff_duration = |
| base::TimeDelta::FromSecondsD(serialized_backoff_duration_double); |
| EXPECT_EQ(serialized_backoff_duration, test_case.expected_backoff_duration); |
| } |
| } |
| |
| // This test verifies that BackoffEntrySerializer::SerializeToValue will not |
| // serialize an infinite release time. |
| // |
| // In pseudocode, this is how absolute_release_time is computed: |
| // backoff_duration = release_time - now; |
| // absolute_release_time = backoff_duration + original_time; |
| // |
| // This test induces backoff_duration to be a nonzero duration and directly sets |
| // original_time as a large value, such that their addition will overflow. |
| TEST(BackoffEntrySerializerTest, SerializeFiniteReleaseTime) { |
| const TimeTicks release_time = TimeTicks() + TimeDelta::FromMicroseconds(5); |
| const Time original_time = Time::Max() - TimeDelta::FromMicroseconds(4); |
| |
| TestTickClock original_ticks; |
| original_ticks.set_now(TimeTicks()); |
| BackoffEntry original(&base_policy, &original_ticks); |
| original.SetCustomReleaseTime(release_time); |
| std::unique_ptr<base::Value> serialized = |
| BackoffEntrySerializer::SerializeToValue(original, original_time); |
| |
| // Reach into the serialization and check the string-formatted release time. |
| const std::string& serialized_release_time = |
| serialized->GetList()[3].GetString(); |
| EXPECT_EQ(serialized_release_time, "0"); |
| |
| // Test that |DeserializeFromValue| notices this zero-valued release time and |
| // does not take it at face value. |
| const Time parse_time = |
| Time::FromJsTime(1430907555111); // May 2015 for realism |
| std::unique_ptr<BackoffEntry> deserialized = |
| BackoffEntrySerializer::DeserializeFromValue(*serialized, &base_policy, |
| &original_ticks, parse_time); |
| ASSERT_TRUE(deserialized.get()); |
| EXPECT_EQ(original.GetReleaseTime(), deserialized->GetReleaseTime()); |
| } |
| |
| TEST(BackoffEntrySerializerTest, SerializeNoFailures) { |
| Time original_time = Time::Now(); |
| TestTickClock original_ticks; |
| original_ticks.set_now(TimeTicks::Now()); |
| BackoffEntry original(&base_policy, &original_ticks); |
| std::unique_ptr<base::Value> serialized = |
| BackoffEntrySerializer::SerializeToValue(original, original_time); |
| |
| std::unique_ptr<BackoffEntry> deserialized = |
| BackoffEntrySerializer::DeserializeFromValue( |
| *serialized, &base_policy, &original_ticks, original_time); |
| ASSERT_TRUE(deserialized.get()); |
| EXPECT_EQ(original.failure_count(), deserialized->failure_count()); |
| EXPECT_EQ(original.GetReleaseTime(), deserialized->GetReleaseTime()); |
| } |
| |
| TEST(BackoffEntrySerializerTest, SerializeTimeOffsets) { |
| Time original_time = Time::FromJsTime(1430907555111); // May 2015 for realism |
| TestTickClock original_ticks; |
| BackoffEntry original(&base_policy, &original_ticks); |
| // 2 errors. |
| original.InformOfRequest(false); |
| original.InformOfRequest(false); |
| std::unique_ptr<base::Value> serialized = |
| BackoffEntrySerializer::SerializeToValue(original, original_time); |
| |
| { |
| // Test that immediate deserialization round-trips. |
| std::unique_ptr<BackoffEntry> deserialized = |
| BackoffEntrySerializer::DeserializeFromValue( |
| *serialized, &base_policy, &original_ticks, original_time); |
| ASSERT_TRUE(deserialized.get()); |
| EXPECT_EQ(original.failure_count(), deserialized->failure_count()); |
| EXPECT_EQ(original.GetReleaseTime(), deserialized->GetReleaseTime()); |
| } |
| |
| { |
| // Test deserialization when wall clock has advanced but TimeTicks::Now() |
| // hasn't (e.g. device was rebooted). |
| Time later_time = original_time + TimeDelta::FromDays(1); |
| std::unique_ptr<BackoffEntry> deserialized = |
| BackoffEntrySerializer::DeserializeFromValue( |
| *serialized, &base_policy, &original_ticks, later_time); |
| ASSERT_TRUE(deserialized.get()); |
| EXPECT_EQ(original.failure_count(), deserialized->failure_count()); |
| // Remaining backoff duration continues decreasing while device is off. |
| // Since TimeTicks::Now() has not advanced, the absolute release time ticks |
| // will decrease accordingly. |
| EXPECT_GT(original.GetTimeUntilRelease(), |
| deserialized->GetTimeUntilRelease()); |
| EXPECT_EQ(original.GetReleaseTime() - TimeDelta::FromDays(1), |
| deserialized->GetReleaseTime()); |
| } |
| |
| { |
| // Test deserialization when TimeTicks::Now() has advanced but wall clock |
| // hasn't (e.g. it's an hour later, but a DST change cancelled that out). |
| TestTickClock later_ticks; |
| later_ticks.set_now(TimeTicks() + TimeDelta::FromDays(1)); |
| std::unique_ptr<BackoffEntry> deserialized = |
| BackoffEntrySerializer::DeserializeFromValue( |
| *serialized, &base_policy, &later_ticks, original_time); |
| ASSERT_TRUE(deserialized.get()); |
| EXPECT_EQ(original.failure_count(), deserialized->failure_count()); |
| // According to the wall clock, no time has passed. So remaining backoff |
| // duration is preserved, hence the absolute release time ticks increases. |
| // This isn't ideal - by also serializing the current time and time ticks, |
| // it would be possible to detect that time has passed but the wall clock |
| // went backwards, and reduce the remaining backoff duration accordingly, |
| // however the current implementation does not do this as the benefit would |
| // be somewhat marginal. |
| EXPECT_EQ(original.GetTimeUntilRelease(), |
| deserialized->GetTimeUntilRelease()); |
| EXPECT_EQ(original.GetReleaseTime() + TimeDelta::FromDays(1), |
| deserialized->GetReleaseTime()); |
| } |
| |
| { |
| // Test deserialization when both wall clock and TimeTicks::Now() have |
| // advanced (e.g. it's just later than it used to be). |
| TestTickClock later_ticks; |
| later_ticks.set_now(TimeTicks() + TimeDelta::FromDays(1)); |
| Time later_time = original_time + TimeDelta::FromDays(1); |
| std::unique_ptr<BackoffEntry> deserialized = |
| BackoffEntrySerializer::DeserializeFromValue(*serialized, &base_policy, |
| &later_ticks, later_time); |
| ASSERT_TRUE(deserialized.get()); |
| EXPECT_EQ(original.failure_count(), deserialized->failure_count()); |
| // Since both have advanced by the same amount, the absolute release time |
| // ticks should be preserved; the remaining backoff duration will have |
| // decreased of course, since time has passed. |
| EXPECT_GT(original.GetTimeUntilRelease(), |
| deserialized->GetTimeUntilRelease()); |
| EXPECT_EQ(original.GetReleaseTime(), deserialized->GetReleaseTime()); |
| } |
| |
| { |
| // Test deserialization when wall clock has gone backwards but TimeTicks |
| // haven't (e.g. the system clock was fast but they fixed it). |
| EXPECT_LT(TimeDelta::FromSeconds(1), original.GetTimeUntilRelease()); |
| Time earlier_time = original_time - TimeDelta::FromSeconds(1); |
| std::unique_ptr<BackoffEntry> deserialized = |
| BackoffEntrySerializer::DeserializeFromValue( |
| *serialized, &base_policy, &original_ticks, earlier_time); |
| ASSERT_TRUE(deserialized.get()); |
| EXPECT_EQ(original.failure_count(), deserialized->failure_count()); |
| // If only the absolute wall clock time was serialized, subtracting the |
| // (decreased) current wall clock time from the serialized wall clock time |
| // could give very large (incorrect) values for remaining backoff duration. |
| // But instead the implementation also serializes the remaining backoff |
| // duration, and doesn't allow the duration to increase beyond it's previous |
| // value during deserialization. Hence when the wall clock goes backwards |
| // the remaining backoff duration will be preserved. |
| EXPECT_EQ(original.GetTimeUntilRelease(), |
| deserialized->GetTimeUntilRelease()); |
| // Since TimeTicks::Now() hasn't changed, the absolute release time ticks |
| // will be equal too in this particular case. |
| EXPECT_EQ(original.GetReleaseTime(), deserialized->GetReleaseTime()); |
| } |
| } |
| |
| } // namespace |
| |
| } // namespace net |