| /* |
| * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| #include "net/dcsctp/tx/retransmission_timeout.h" |
| |
| #include "net/dcsctp/public/dcsctp_options.h" |
| #include "rtc_base/gunit.h" |
| #include "test/gmock.h" |
| |
| namespace dcsctp { |
| namespace { |
| using ::webrtc::TimeDelta; |
| |
| constexpr TimeDelta kMaxRtt = TimeDelta::Millis(8'000); |
| constexpr TimeDelta kInitialRto = TimeDelta::Millis(200); |
| constexpr TimeDelta kMaxRto = TimeDelta::Millis(800); |
| constexpr TimeDelta kMinRto = TimeDelta::Millis(120); |
| constexpr TimeDelta kMinRttVariance = TimeDelta::Millis(220); |
| |
| DcSctpOptions MakeOptions() { |
| DcSctpOptions options; |
| options.rtt_max = DurationMs(kMaxRtt); |
| options.rto_initial = DurationMs(kInitialRto); |
| options.rto_max = DurationMs(kMaxRto); |
| options.rto_min = DurationMs(kMinRto); |
| options.min_rtt_variance = DurationMs(kMinRttVariance); |
| return options; |
| } |
| |
| TEST(RetransmissionTimeoutTest, HasValidInitialRto) { |
| RetransmissionTimeout rto_(MakeOptions()); |
| EXPECT_EQ(rto_.rto(), kInitialRto); |
| } |
| |
| TEST(RetransmissionTimeoutTest, HasValidInitialSrtt) { |
| RetransmissionTimeout rto_(MakeOptions()); |
| EXPECT_EQ(rto_.srtt(), kInitialRto); |
| } |
| |
| TEST(RetransmissionTimeoutTest, NegativeValuesDoNotAffectRTO) { |
| RetransmissionTimeout rto_(MakeOptions()); |
| // Initial negative value |
| rto_.ObserveRTT(TimeDelta::Millis(-10)); |
| EXPECT_EQ(rto_.rto(), kInitialRto); |
| rto_.ObserveRTT(TimeDelta::Millis(124)); |
| EXPECT_EQ(rto_.rto(), TimeDelta::Millis(372)); |
| // Subsequent negative value |
| rto_.ObserveRTT(TimeDelta::Millis(-10)); |
| EXPECT_EQ(rto_.rto(), TimeDelta::Millis(372)); |
| } |
| |
| TEST(RetransmissionTimeoutTest, TooLargeValuesDoNotAffectRTO) { |
| RetransmissionTimeout rto_(MakeOptions()); |
| // Initial too large value |
| rto_.ObserveRTT(kMaxRtt + TimeDelta::Millis(100)); |
| EXPECT_EQ(rto_.rto(), kInitialRto); |
| rto_.ObserveRTT(TimeDelta::Millis(124)); |
| EXPECT_EQ(rto_.rto(), TimeDelta::Millis(372)); |
| // Subsequent too large value |
| rto_.ObserveRTT(kMaxRtt + TimeDelta::Millis(100)); |
| EXPECT_EQ(rto_.rto(), TimeDelta::Millis(372)); |
| } |
| |
| TEST(RetransmissionTimeoutTest, WillNeverGoBelowMinimumRto) { |
| RetransmissionTimeout rto_(MakeOptions()); |
| for (int i = 0; i < 1000; ++i) { |
| rto_.ObserveRTT(TimeDelta::Millis(1)); |
| } |
| EXPECT_GE(rto_.rto(), kMinRto); |
| } |
| |
| TEST(RetransmissionTimeoutTest, WillNeverGoAboveMaximumRto) { |
| RetransmissionTimeout rto_(MakeOptions()); |
| for (int i = 0; i < 1000; ++i) { |
| rto_.ObserveRTT(kMaxRtt - TimeDelta::Millis(1)); |
| // Adding jitter, which would make it RTO be well above RTT. |
| rto_.ObserveRTT(kMaxRtt - TimeDelta::Millis(100)); |
| } |
| EXPECT_LE(rto_.rto(), kMaxRto); |
| } |
| |
| TEST(RetransmissionTimeoutTest, CalculatesRtoForStableRtt) { |
| RetransmissionTimeout rto_(MakeOptions()); |
| rto_.ObserveRTT(TimeDelta::Millis(124)); |
| EXPECT_EQ(rto_.rto(), TimeDelta::Millis(372)); |
| rto_.ObserveRTT(TimeDelta::Millis(128)); |
| EXPECT_EQ(rto_.rto(), TimeDelta::Millis(344)); |
| rto_.ObserveRTT(TimeDelta::Millis(123)); |
| EXPECT_EQ(rto_.rto(), TimeDelta::Millis(344)); |
| rto_.ObserveRTT(TimeDelta::Millis(125)); |
| EXPECT_EQ(rto_.rto(), TimeDelta::Millis(344)); |
| rto_.ObserveRTT(TimeDelta::Millis(127)); |
| EXPECT_EQ(rto_.rto(), TimeDelta::Millis(344)); |
| } |
| |
| TEST(RetransmissionTimeoutTest, CalculatesRtoForUnstableRtt) { |
| RetransmissionTimeout rto_(MakeOptions()); |
| rto_.ObserveRTT(TimeDelta::Millis(124)); |
| EXPECT_EQ(rto_.rto(), TimeDelta::Millis(372)); |
| rto_.ObserveRTT(TimeDelta::Millis(402)); |
| EXPECT_EQ(rto_.rto(), TimeDelta::Millis(622)); |
| rto_.ObserveRTT(TimeDelta::Millis(728)); |
| EXPECT_EQ(rto_.rto(), TimeDelta::Millis(800)); |
| rto_.ObserveRTT(TimeDelta::Millis(89)); |
| EXPECT_EQ(rto_.rto(), TimeDelta::Millis(800)); |
| rto_.ObserveRTT(TimeDelta::Millis(126)); |
| EXPECT_EQ(rto_.rto(), TimeDelta::Millis(800)); |
| } |
| |
| TEST(RetransmissionTimeoutTest, WillStabilizeAfterAWhile) { |
| RetransmissionTimeout rto_(MakeOptions()); |
| rto_.ObserveRTT(TimeDelta::Millis(124)); |
| rto_.ObserveRTT(TimeDelta::Millis(402)); |
| rto_.ObserveRTT(TimeDelta::Millis(728)); |
| rto_.ObserveRTT(TimeDelta::Millis(89)); |
| rto_.ObserveRTT(TimeDelta::Millis(126)); |
| EXPECT_EQ(rto_.rto(), TimeDelta::Millis(800)); |
| rto_.ObserveRTT(TimeDelta::Millis(124)); |
| EXPECT_EQ(rto_.rto(), TimeDelta::Millis(800)); |
| rto_.ObserveRTT(TimeDelta::Millis(122)); |
| EXPECT_EQ(rto_.rto(), TimeDelta::Millis(710)); |
| rto_.ObserveRTT(TimeDelta::Millis(123)); |
| EXPECT_EQ(rto_.rto(), TimeDelta::Millis(631)); |
| rto_.ObserveRTT(TimeDelta::Millis(124)); |
| EXPECT_EQ(rto_.rto(), TimeDelta::Millis(562)); |
| rto_.ObserveRTT(TimeDelta::Millis(122)); |
| EXPECT_EQ(rto_.rto(), TimeDelta::Millis(505)); |
| rto_.ObserveRTT(TimeDelta::Millis(124)); |
| EXPECT_EQ(rto_.rto(), TimeDelta::Millis(454)); |
| rto_.ObserveRTT(TimeDelta::Millis(124)); |
| EXPECT_EQ(rto_.rto(), TimeDelta::Millis(410)); |
| rto_.ObserveRTT(TimeDelta::Millis(124)); |
| EXPECT_EQ(rto_.rto(), TimeDelta::Millis(372)); |
| rto_.ObserveRTT(TimeDelta::Millis(124)); |
| EXPECT_EQ(rto_.rto(), TimeDelta::Millis(367)); |
| } |
| |
| TEST(RetransmissionTimeoutTest, WillAlwaysStayAboveRTT) { |
| // In simulations, it's quite common to have a very stable RTT, and having an |
| // RTO at the same value will cause issues as expiry timers will be scheduled |
| // to be expire exactly when a packet is supposed to arrive. The RTO must be |
| // larger than the RTT. In non-simulated environments, this is a non-issue as |
| // any jitter will increase the RTO. |
| RetransmissionTimeout rto_(MakeOptions()); |
| |
| for (int i = 0; i < 1000; ++i) { |
| rto_.ObserveRTT(TimeDelta::Millis(124)); |
| } |
| EXPECT_EQ(rto_.rto(), TimeDelta::Millis(344)); |
| } |
| |
| TEST(RetransmissionTimeoutTest, CanSpecifySmallerMinimumRttVariance) { |
| DcSctpOptions options = MakeOptions(); |
| options.min_rtt_variance = |
| DurationMs(kMinRttVariance - TimeDelta::Millis(100)); |
| RetransmissionTimeout rto_(options); |
| |
| for (int i = 0; i < 1000; ++i) { |
| rto_.ObserveRTT(TimeDelta::Millis(124)); |
| } |
| EXPECT_EQ(rto_.rto(), TimeDelta::Millis(244)); |
| } |
| |
| TEST(RetransmissionTimeoutTest, CanSpecifyLargerMinimumRttVariance) { |
| DcSctpOptions options = MakeOptions(); |
| options.min_rtt_variance = |
| DurationMs(kMinRttVariance + TimeDelta::Millis(100)); |
| RetransmissionTimeout rto_(options); |
| |
| for (int i = 0; i < 1000; ++i) { |
| rto_.ObserveRTT(TimeDelta::Millis(124)); |
| } |
| EXPECT_EQ(rto_.rto(), TimeDelta::Millis(444)); |
| } |
| |
| } // namespace |
| } // namespace dcsctp |