| // Copyright (c) 2022 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 "quiche/quic/core/quic_ping_manager.h" | |
| #include "quiche/quic/core/quic_connection_alarms.h" | |
| #include "quiche/quic/core/quic_one_block_arena.h" | |
| #include "quiche/quic/platform/api/quic_test.h" | |
| #include "quiche/quic/test_tools/mock_quic_connection_alarms.h" | |
| #include "quiche/quic/test_tools/quic_connection_peer.h" | |
| #include "quiche/quic/test_tools/quic_test_utils.h" | |
| namespace quic { | |
| namespace test { | |
| class QuicPingManagerPeer { | |
| public: | |
| static void SetPerspective(QuicPingManager* manager, | |
| Perspective perspective) { | |
| manager->perspective_ = perspective; | |
| } | |
| }; | |
| namespace { | |
| const bool kShouldKeepAlive = true; | |
| const bool kHasInflightPackets = true; | |
| const QuicTime::Delta kPtoDelay = QuicTime::Delta::FromMilliseconds(50); | |
| class MockDelegate : public QuicPingManager::Delegate { | |
| public: | |
| MOCK_METHOD(void, OnKeepAliveTimeout, (), (override)); | |
| MOCK_METHOD(void, OnRetransmittableOnWireTimeout, (), (override)); | |
| }; | |
| class QuicPingManagerTest : public QuicTest { | |
| public: | |
| QuicPingManagerTest() | |
| : alarms_(&connection_alarms_delegate_, arena_, alarm_factory_), | |
| alarm_(&alarms_, QuicAlarmSlot::kPing), | |
| manager_(Perspective::IS_CLIENT, &delegate_, alarm_) { | |
| clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1)); | |
| ON_CALL(connection_alarms_delegate_, OnPingAlarm()).WillByDefault([&] { | |
| manager_.OnAlarm(); | |
| }); | |
| } | |
| protected: | |
| testing::StrictMock<MockDelegate> delegate_; | |
| MockConnectionAlarmsDelegate connection_alarms_delegate_; | |
| MockClock clock_; | |
| QuicConnectionArena arena_; | |
| MockAlarmFactory alarm_factory_; | |
| QuicAlarmMultiplexer alarms_; | |
| QuicTestAlarmProxy alarm_; | |
| QuicPingManager manager_; | |
| }; | |
| TEST_F(QuicPingManagerTest, KeepAliveTimeout) { | |
| EXPECT_FALSE(alarm_->IsSet()); | |
| clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); | |
| // Set alarm with in flight packets. | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| kHasInflightPackets, kPtoDelay); | |
| EXPECT_TRUE(alarm_->IsSet()); | |
| EXPECT_EQ(QuicTime::Delta::FromSeconds(kPingTimeoutSecs), | |
| alarm_->deadline() - clock_.ApproximateNow()); | |
| clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); | |
| // Reset alarm with no in flight packets. | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| !kHasInflightPackets, kPtoDelay); | |
| EXPECT_TRUE(alarm_->IsSet()); | |
| // Verify the deadline is set slightly less than 15 seconds in the future, | |
| // because of the 1s alarm granularity. | |
| EXPECT_EQ(QuicTime::Delta::FromSeconds(kPingTimeoutSecs) - | |
| QuicTime::Delta::FromMilliseconds(5), | |
| alarm_->deadline() - clock_.ApproximateNow()); | |
| clock_.AdvanceTime(QuicTime::Delta::FromSeconds(kPingTimeoutSecs)); | |
| EXPECT_CALL(delegate_, OnKeepAliveTimeout()); | |
| alarm_->Fire(); | |
| EXPECT_FALSE(alarm_->IsSet()); | |
| // Reset alarm with in flight packets. | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| kHasInflightPackets, kPtoDelay); | |
| EXPECT_TRUE(alarm_->IsSet()); | |
| // Verify alarm is not armed if !kShouldKeepAlive. | |
| clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); | |
| manager_.SetAlarm(clock_.ApproximateNow(), !kShouldKeepAlive, | |
| kHasInflightPackets, kPtoDelay); | |
| EXPECT_FALSE(alarm_->IsSet()); | |
| } | |
| TEST_F(QuicPingManagerTest, CustomizedKeepAliveTimeout) { | |
| EXPECT_FALSE(alarm_->IsSet()); | |
| // Set customized keep-alive timeout. | |
| manager_.set_keep_alive_timeout(QuicTime::Delta::FromSeconds(10)); | |
| clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); | |
| // Set alarm with in flight packets. | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| kHasInflightPackets, kPtoDelay); | |
| EXPECT_TRUE(alarm_->IsSet()); | |
| EXPECT_EQ(QuicTime::Delta::FromSeconds(10), | |
| alarm_->deadline() - clock_.ApproximateNow()); | |
| clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); | |
| // Set alarm with no in flight packets. | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| !kHasInflightPackets, kPtoDelay); | |
| EXPECT_TRUE(alarm_->IsSet()); | |
| // The deadline is set slightly less than 10 seconds in the future, because | |
| // of the 1s alarm granularity. | |
| EXPECT_EQ( | |
| QuicTime::Delta::FromSeconds(10) - QuicTime::Delta::FromMilliseconds(5), | |
| alarm_->deadline() - clock_.ApproximateNow()); | |
| clock_.AdvanceTime(QuicTime::Delta::FromSeconds(10)); | |
| EXPECT_CALL(delegate_, OnKeepAliveTimeout()); | |
| alarm_->Fire(); | |
| EXPECT_FALSE(alarm_->IsSet()); | |
| // Reset alarm with in flight packets. | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| kHasInflightPackets, kPtoDelay); | |
| EXPECT_TRUE(alarm_->IsSet()); | |
| // Verify alarm is not armed if !kShouldKeepAlive. | |
| clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); | |
| manager_.SetAlarm(clock_.ApproximateNow(), !kShouldKeepAlive, | |
| kHasInflightPackets, kPtoDelay); | |
| EXPECT_FALSE(alarm_->IsSet()); | |
| } | |
| TEST_F(QuicPingManagerTest, RetransmittableOnWireTimeout) { | |
| const QuicTime::Delta kRtransmittableOnWireTimeout = | |
| QuicTime::Delta::FromMilliseconds(50); | |
| manager_.set_initial_retransmittable_on_wire_timeout( | |
| kRtransmittableOnWireTimeout); | |
| EXPECT_FALSE(alarm_->IsSet()); | |
| // Set alarm with in flight packets. | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| kHasInflightPackets, kPtoDelay); | |
| // Verify alarm is in keep-alive mode. | |
| EXPECT_TRUE(alarm_->IsSet()); | |
| EXPECT_EQ(QuicTime::Delta::FromSeconds(kPingTimeoutSecs), | |
| alarm_->deadline() - clock_.ApproximateNow()); | |
| clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); | |
| // Set alarm with no in flight packets. | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| !kHasInflightPackets, kPtoDelay); | |
| EXPECT_TRUE(alarm_->IsSet()); | |
| // Verify alarm is in retransmittable-on-wire mode. | |
| EXPECT_EQ(kRtransmittableOnWireTimeout, | |
| alarm_->deadline() - clock_.ApproximateNow()); | |
| clock_.AdvanceTime(kRtransmittableOnWireTimeout); | |
| EXPECT_CALL(delegate_, OnRetransmittableOnWireTimeout()); | |
| alarm_->Fire(); | |
| EXPECT_FALSE(alarm_->IsSet()); | |
| // Reset alarm with in flight packets. | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| kHasInflightPackets, kPtoDelay); | |
| // Verify the alarm is in keep-alive mode. | |
| ASSERT_TRUE(alarm_->IsSet()); | |
| EXPECT_EQ(QuicTime::Delta::FromSeconds(kPingTimeoutSecs), | |
| alarm_->deadline() - clock_.ApproximateNow()); | |
| } | |
| TEST_F(QuicPingManagerTest, RetransmittableOnWireTimeoutExponentiallyBackOff) { | |
| const int kMaxAggressiveRetransmittableOnWireCount = 5; | |
| SetQuicFlag(quic_max_aggressive_retransmittable_on_wire_ping_count, | |
| kMaxAggressiveRetransmittableOnWireCount); | |
| const QuicTime::Delta initial_retransmittable_on_wire_timeout = | |
| QuicTime::Delta::FromMilliseconds(200); | |
| manager_.set_initial_retransmittable_on_wire_timeout( | |
| initial_retransmittable_on_wire_timeout); | |
| clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); | |
| EXPECT_FALSE(alarm_->IsSet()); | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| kHasInflightPackets, kPtoDelay); | |
| // Verify alarm is in keep-alive mode. | |
| EXPECT_TRUE(alarm_->IsSet()); | |
| EXPECT_EQ(QuicTime::Delta::FromSeconds(kPingTimeoutSecs), | |
| alarm_->deadline() - clock_.ApproximateNow()); | |
| // Verify no exponential backoff on the first few retransmittable on wire | |
| // timeouts. | |
| for (int i = 0; i <= kMaxAggressiveRetransmittableOnWireCount; ++i) { | |
| clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); | |
| // Reset alarm with no in flight packets. | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| !kHasInflightPackets, kPtoDelay); | |
| EXPECT_TRUE(alarm_->IsSet()); | |
| // Verify alarm is in retransmittable-on-wire mode. | |
| EXPECT_EQ(initial_retransmittable_on_wire_timeout, | |
| alarm_->deadline() - clock_.ApproximateNow()); | |
| clock_.AdvanceTime(initial_retransmittable_on_wire_timeout); | |
| EXPECT_CALL(delegate_, OnRetransmittableOnWireTimeout()); | |
| alarm_->Fire(); | |
| EXPECT_FALSE(alarm_->IsSet()); | |
| // Reset alarm with in flight packets. | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| kHasInflightPackets, kPtoDelay); | |
| } | |
| QuicTime::Delta retransmittable_on_wire_timeout = | |
| initial_retransmittable_on_wire_timeout; | |
| // Verify subsequent retransmittable-on-wire timeout is exponentially backed | |
| // off. | |
| while (retransmittable_on_wire_timeout * 2 < | |
| QuicTime::Delta::FromSeconds(kPingTimeoutSecs)) { | |
| retransmittable_on_wire_timeout = retransmittable_on_wire_timeout * 2; | |
| clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| !kHasInflightPackets, kPtoDelay); | |
| EXPECT_TRUE(alarm_->IsSet()); | |
| EXPECT_EQ(retransmittable_on_wire_timeout, | |
| alarm_->deadline() - clock_.ApproximateNow()); | |
| clock_.AdvanceTime(retransmittable_on_wire_timeout); | |
| EXPECT_CALL(delegate_, OnRetransmittableOnWireTimeout()); | |
| alarm_->Fire(); | |
| EXPECT_FALSE(alarm_->IsSet()); | |
| // Reset alarm with in flight packets. | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| kHasInflightPackets, kPtoDelay); | |
| } | |
| // Verify alarm is in keep-alive mode. | |
| EXPECT_TRUE(alarm_->IsSet()); | |
| EXPECT_EQ(QuicTime::Delta::FromSeconds(kPingTimeoutSecs), | |
| alarm_->deadline() - clock_.ApproximateNow()); | |
| clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); | |
| // Reset alarm with no in flight packets | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| !kHasInflightPackets, kPtoDelay); | |
| EXPECT_TRUE(alarm_->IsSet()); | |
| // Verify alarm is in keep-alive mode because retransmittable-on-wire deadline | |
| // is later. | |
| EXPECT_EQ(QuicTime::Delta::FromSeconds(kPingTimeoutSecs) - | |
| QuicTime::Delta::FromMilliseconds(5), | |
| alarm_->deadline() - clock_.ApproximateNow()); | |
| clock_.AdvanceTime(QuicTime::Delta::FromSeconds(kPingTimeoutSecs) - | |
| QuicTime::Delta::FromMilliseconds(5)); | |
| EXPECT_CALL(delegate_, OnKeepAliveTimeout()); | |
| alarm_->Fire(); | |
| EXPECT_FALSE(alarm_->IsSet()); | |
| } | |
| TEST_F(QuicPingManagerTest, | |
| ResetRetransmitableOnWireTimeoutExponentiallyBackOff) { | |
| const int kMaxAggressiveRetransmittableOnWireCount = 3; | |
| SetQuicFlag(quic_max_aggressive_retransmittable_on_wire_ping_count, | |
| kMaxAggressiveRetransmittableOnWireCount); | |
| const QuicTime::Delta initial_retransmittable_on_wire_timeout = | |
| QuicTime::Delta::FromMilliseconds(200); | |
| manager_.set_initial_retransmittable_on_wire_timeout( | |
| initial_retransmittable_on_wire_timeout); | |
| clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); | |
| EXPECT_FALSE(alarm_->IsSet()); | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| kHasInflightPackets, kPtoDelay); | |
| // Verify alarm is in keep-alive mode. | |
| EXPECT_TRUE(alarm_->IsSet()); | |
| EXPECT_EQ(QuicTime::Delta::FromSeconds(kPingTimeoutSecs), | |
| alarm_->deadline() - clock_.ApproximateNow()); | |
| clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| !kHasInflightPackets, kPtoDelay); | |
| EXPECT_TRUE(alarm_->IsSet()); | |
| // Verify alarm is in retransmittable-on-wire mode. | |
| EXPECT_EQ(initial_retransmittable_on_wire_timeout, | |
| alarm_->deadline() - clock_.ApproximateNow()); | |
| EXPECT_CALL(delegate_, OnRetransmittableOnWireTimeout()); | |
| clock_.AdvanceTime(initial_retransmittable_on_wire_timeout); | |
| alarm_->Fire(); | |
| clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| !kHasInflightPackets, kPtoDelay); | |
| EXPECT_TRUE(alarm_->IsSet()); | |
| EXPECT_EQ(initial_retransmittable_on_wire_timeout, | |
| alarm_->deadline() - clock_.ApproximateNow()); | |
| manager_.reset_consecutive_retransmittable_on_wire_count(); | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| !kHasInflightPackets, kPtoDelay); | |
| EXPECT_EQ(initial_retransmittable_on_wire_timeout, | |
| alarm_->deadline() - clock_.ApproximateNow()); | |
| EXPECT_CALL(delegate_, OnRetransmittableOnWireTimeout()); | |
| clock_.AdvanceTime(initial_retransmittable_on_wire_timeout); | |
| alarm_->Fire(); | |
| for (int i = 0; i < kMaxAggressiveRetransmittableOnWireCount; i++) { | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| !kHasInflightPackets, kPtoDelay); | |
| EXPECT_TRUE(alarm_->IsSet()); | |
| EXPECT_EQ(initial_retransmittable_on_wire_timeout, | |
| alarm_->deadline() - clock_.ApproximateNow()); | |
| clock_.AdvanceTime(initial_retransmittable_on_wire_timeout); | |
| EXPECT_CALL(delegate_, OnRetransmittableOnWireTimeout()); | |
| alarm_->Fire(); | |
| // Reset alarm with in flight packets. | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| kHasInflightPackets, kPtoDelay); | |
| // Advance 5ms to receive next packet. | |
| clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); | |
| } | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| !kHasInflightPackets, kPtoDelay); | |
| EXPECT_TRUE(alarm_->IsSet()); | |
| EXPECT_EQ(initial_retransmittable_on_wire_timeout * 2, | |
| alarm_->deadline() - clock_.ApproximateNow()); | |
| clock_.AdvanceTime(2 * initial_retransmittable_on_wire_timeout); | |
| EXPECT_CALL(delegate_, OnRetransmittableOnWireTimeout()); | |
| alarm_->Fire(); | |
| clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); | |
| manager_.reset_consecutive_retransmittable_on_wire_count(); | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| !kHasInflightPackets, kPtoDelay); | |
| EXPECT_TRUE(alarm_->IsSet()); | |
| EXPECT_EQ(initial_retransmittable_on_wire_timeout, | |
| alarm_->deadline() - clock_.ApproximateNow()); | |
| } | |
| TEST_F(QuicPingManagerTest, RetransmittableOnWireLimit) { | |
| static constexpr int kMaxRetransmittableOnWirePingCount = 3; | |
| SetQuicFlag(quic_max_retransmittable_on_wire_ping_count, | |
| kMaxRetransmittableOnWirePingCount); | |
| static constexpr QuicTime::Delta initial_retransmittable_on_wire_timeout = | |
| QuicTime::Delta::FromMilliseconds(200); | |
| static constexpr QuicTime::Delta kShortDelay = | |
| QuicTime::Delta::FromMilliseconds(5); | |
| ASSERT_LT(kShortDelay * 10, initial_retransmittable_on_wire_timeout); | |
| manager_.set_initial_retransmittable_on_wire_timeout( | |
| initial_retransmittable_on_wire_timeout); | |
| clock_.AdvanceTime(kShortDelay); | |
| EXPECT_FALSE(alarm_->IsSet()); | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| kHasInflightPackets, kPtoDelay); | |
| EXPECT_TRUE(alarm_->IsSet()); | |
| EXPECT_EQ(QuicTime::Delta::FromSeconds(kPingTimeoutSecs), | |
| alarm_->deadline() - clock_.ApproximateNow()); | |
| for (int i = 0; i <= kMaxRetransmittableOnWirePingCount; i++) { | |
| clock_.AdvanceTime(kShortDelay); | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| !kHasInflightPackets, kPtoDelay); | |
| EXPECT_TRUE(alarm_->IsSet()); | |
| EXPECT_EQ(initial_retransmittable_on_wire_timeout, | |
| alarm_->deadline() - clock_.ApproximateNow()); | |
| clock_.AdvanceTime(initial_retransmittable_on_wire_timeout); | |
| EXPECT_CALL(delegate_, OnRetransmittableOnWireTimeout()); | |
| alarm_->Fire(); | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| kHasInflightPackets, kPtoDelay); | |
| } | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| !kHasInflightPackets, kPtoDelay); | |
| EXPECT_TRUE(alarm_->IsSet()); | |
| // Verify alarm is in keep-alive mode. | |
| EXPECT_EQ(QuicTime::Delta::FromSeconds(kPingTimeoutSecs), | |
| alarm_->deadline() - clock_.ApproximateNow()); | |
| clock_.AdvanceTime(QuicTime::Delta::FromSeconds(kPingTimeoutSecs)); | |
| EXPECT_CALL(delegate_, OnKeepAliveTimeout()); | |
| alarm_->Fire(); | |
| EXPECT_FALSE(alarm_->IsSet()); | |
| } | |
| TEST_F(QuicPingManagerTest, MaxRetransmittableOnWireDelayShift) { | |
| QuicPingManagerPeer::SetPerspective(&manager_, Perspective::IS_SERVER); | |
| const int kMaxAggressiveRetransmittableOnWireCount = 3; | |
| SetQuicFlag(quic_max_aggressive_retransmittable_on_wire_ping_count, | |
| kMaxAggressiveRetransmittableOnWireCount); | |
| const QuicTime::Delta initial_retransmittable_on_wire_timeout = | |
| QuicTime::Delta::FromMilliseconds(200); | |
| manager_.set_initial_retransmittable_on_wire_timeout( | |
| initial_retransmittable_on_wire_timeout); | |
| for (int i = 0; i <= kMaxAggressiveRetransmittableOnWireCount; i++) { | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| !kHasInflightPackets, kPtoDelay); | |
| EXPECT_TRUE(alarm_->IsSet()); | |
| EXPECT_EQ(initial_retransmittable_on_wire_timeout, | |
| alarm_->deadline() - clock_.ApproximateNow()); | |
| clock_.AdvanceTime(initial_retransmittable_on_wire_timeout); | |
| EXPECT_CALL(delegate_, OnRetransmittableOnWireTimeout()); | |
| alarm_->Fire(); | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| kHasInflightPackets, kPtoDelay); | |
| } | |
| for (int i = 1; i <= 20; ++i) { | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| !kHasInflightPackets, kPtoDelay); | |
| EXPECT_TRUE(alarm_->IsSet()); | |
| if (i <= 10) { | |
| EXPECT_EQ(initial_retransmittable_on_wire_timeout * (1 << i), | |
| alarm_->deadline() - clock_.ApproximateNow()); | |
| } else { | |
| // Verify shift is capped. | |
| EXPECT_EQ(initial_retransmittable_on_wire_timeout * (1 << 10), | |
| alarm_->deadline() - clock_.ApproximateNow()); | |
| } | |
| clock_.AdvanceTime(alarm_->deadline() - clock_.ApproximateNow()); | |
| EXPECT_CALL(delegate_, OnRetransmittableOnWireTimeout()); | |
| alarm_->Fire(); | |
| } | |
| } | |
| TEST_F(QuicPingManagerTest, PtoBasedRetransmittableOnWireTimeout) { | |
| // Set the initial retransmittable on wire timeout. | |
| manager_.set_initial_retransmittable_on_wire_timeout( | |
| QuicTime::Delta::FromMilliseconds(200)); | |
| // Verify the alarm is set based on different value based on PTO delay. | |
| for (int num_times_pto : {1, 2, 3}) { | |
| EXPECT_TRUE(!alarm_->IsSet()); | |
| manager_.set_num_ptos_for_retransmittable_on_wire_timeout(num_times_pto); | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| !kHasInflightPackets, kPtoDelay); | |
| EXPECT_EQ(kPtoDelay * num_times_pto, | |
| alarm_->deadline() - clock_.ApproximateNow()); | |
| EXPECT_TRUE(alarm_->IsSet()); | |
| clock_.AdvanceTime(kPtoDelay * num_times_pto); | |
| EXPECT_CALL(delegate_, OnRetransmittableOnWireTimeout()); | |
| alarm_->Fire(); | |
| } | |
| } | |
| TEST_F(QuicPingManagerTest, | |
| PtoBasedRetransmittableOnWireTimeoutExponentiallyBackOff) { | |
| const int kMaxAggressiveRetransmittableOnWireCount = 5; | |
| SetQuicFlag(quic_max_aggressive_retransmittable_on_wire_ping_count, | |
| kMaxAggressiveRetransmittableOnWireCount); | |
| for (int num_times_pto : {1, 2, 3}) { | |
| manager_.set_num_ptos_for_retransmittable_on_wire_timeout(num_times_pto); | |
| QuicTime::Delta pto_delay = static_cast<int>(num_times_pto) * kPtoDelay; | |
| clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); | |
| EXPECT_FALSE(alarm_->IsSet()); | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| kHasInflightPackets, kPtoDelay); | |
| // Verify alarm is in keep-alive mode. | |
| EXPECT_TRUE(alarm_->IsSet()); | |
| EXPECT_EQ(QuicTime::Delta::FromSeconds(kPingTimeoutSecs), | |
| alarm_->deadline() - clock_.ApproximateNow()); | |
| // Verify no exponential backoff on the first few retransmittable on wire | |
| // timeouts. | |
| for (int i = 0; i <= kMaxAggressiveRetransmittableOnWireCount; ++i) { | |
| clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); | |
| // Reset alarm with no in flight packets. | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| !kHasInflightPackets, kPtoDelay); | |
| EXPECT_TRUE(alarm_->IsSet()); | |
| // Verify alarm is in retransmittable-on-wire mode. | |
| EXPECT_EQ(pto_delay, alarm_->deadline() - clock_.ApproximateNow()); | |
| clock_.AdvanceTime(pto_delay); | |
| EXPECT_CALL(delegate_, OnRetransmittableOnWireTimeout()); | |
| alarm_->Fire(); | |
| EXPECT_FALSE(alarm_->IsSet()); | |
| // Reset alarm with in flight packets. | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| kHasInflightPackets, kPtoDelay); | |
| } | |
| QuicTime::Delta retransmittable_on_wire_timeout = pto_delay; | |
| // Verify subsequent retransmittable-on-wire timeout is exponentially | |
| // backed off. | |
| while (retransmittable_on_wire_timeout * 2 < | |
| QuicTime::Delta::FromSeconds(kPingTimeoutSecs)) { | |
| retransmittable_on_wire_timeout = retransmittable_on_wire_timeout * 2; | |
| clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| !kHasInflightPackets, kPtoDelay); | |
| EXPECT_TRUE(alarm_->IsSet()); | |
| EXPECT_EQ(retransmittable_on_wire_timeout, | |
| alarm_->deadline() - clock_.ApproximateNow()); | |
| clock_.AdvanceTime(retransmittable_on_wire_timeout); | |
| EXPECT_CALL(delegate_, OnRetransmittableOnWireTimeout()); | |
| alarm_->Fire(); | |
| EXPECT_FALSE(alarm_->IsSet()); | |
| // Reset alarm with in flight packets. | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| kHasInflightPackets, kPtoDelay); | |
| } | |
| // Verify alarm is in keep-alive mode. | |
| EXPECT_TRUE(alarm_->IsSet()); | |
| EXPECT_EQ(QuicTime::Delta::FromSeconds(kPingTimeoutSecs), | |
| alarm_->deadline() - clock_.ApproximateNow()); | |
| clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); | |
| // Reset alarm with no in flight packets | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| !kHasInflightPackets, kPtoDelay); | |
| EXPECT_TRUE(alarm_->IsSet()); | |
| // Verify alarm is in keep-alive mode because retransmittable-on-wire | |
| // deadline is later. | |
| EXPECT_EQ(QuicTime::Delta::FromSeconds(kPingTimeoutSecs) - | |
| QuicTime::Delta::FromMilliseconds(5), | |
| alarm_->deadline() - clock_.ApproximateNow()); | |
| clock_.AdvanceTime(QuicTime::Delta::FromSeconds(kPingTimeoutSecs) - | |
| QuicTime::Delta::FromMilliseconds(5)); | |
| EXPECT_CALL(delegate_, OnKeepAliveTimeout()); | |
| alarm_->Fire(); | |
| EXPECT_FALSE(alarm_->IsSet()); | |
| // Reset the consecutive retransmittable on wire count for next iteration. | |
| manager_.reset_consecutive_retransmittable_on_wire_count(); | |
| } | |
| } | |
| TEST_F(QuicPingManagerTest, | |
| SetAlarmAgnosticToRelativeSizeOfKeepAliveTimeoutVsROWTimeout) { | |
| struct TestCase { | |
| QuicTime::Delta retransmittable_on_wire_timeout; | |
| QuicTime::Delta keep_alive_timeout; | |
| }; | |
| static constexpr TestCase kTestCases[] = { | |
| {QuicTime::Delta::FromSeconds(3), QuicTime::Delta::FromSeconds(4)}, | |
| {QuicTime::Delta::FromSeconds(6), QuicTime::Delta::FromSeconds(5)}, | |
| {QuicTime::Delta::FromSeconds(7), QuicTime::Delta::FromSeconds(7)}}; | |
| for (const auto& test_case : kTestCases) { | |
| SCOPED_TRACE(testing::Message() | |
| << "retransmittable_on_wire_timeout: " | |
| << test_case.retransmittable_on_wire_timeout | |
| << ", keep_alive_timeout: " << test_case.keep_alive_timeout); | |
| manager_.Stop(); | |
| alarm_->Cancel(); | |
| EXPECT_FALSE(alarm_->IsSet()); | |
| manager_.set_initial_retransmittable_on_wire_timeout( | |
| test_case.retransmittable_on_wire_timeout); | |
| manager_.set_keep_alive_timeout(test_case.keep_alive_timeout); | |
| manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive, | |
| /*has_in_flight_packets=*/false, kPtoDelay); | |
| EXPECT_TRUE(alarm_->IsSet()); | |
| EXPECT_EQ(alarm_->deadline() - clock_.ApproximateNow(), | |
| std::min(test_case.retransmittable_on_wire_timeout, | |
| test_case.keep_alive_timeout)); | |
| } | |
| } | |
| } // namespace | |
| } // namespace test | |
| } // namespace quic |