blob: d9acc7aa48d01fc8da118e5d1276a646a40f8359 [file] [log] [blame]
// 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_one_block_arena.h"
#include "quiche/quic/platform/api/quic_test.h"
#include "quiche/quic/test_tools/quic_test_utils.h"
namespace quic {
namespace test {
class QuicPingManagerPeer {
public:
static QuicAlarm* GetAlarm(QuicPingManager* manager) {
return manager->alarm_.get();
}
static void SetPerspective(QuicPingManager* manager,
Perspective perspective) {
manager->perspective_ = perspective;
}
};
namespace {
const bool kShouldKeepAlive = true;
const bool kHasInflightPackets = true;
class MockDelegate : public QuicPingManager::Delegate {
public:
MOCK_METHOD(void, OnKeepAliveTimeout, (), (override));
MOCK_METHOD(void, OnRetransmittableOnWireTimeout, (), (override));
};
class QuicPingManagerTest : public QuicTest {
public:
QuicPingManagerTest()
: manager_(Perspective::IS_CLIENT, &delegate_, &arena_, &alarm_factory_,
/*context=*/nullptr),
alarm_(static_cast<MockAlarmFactory::TestAlarm*>(
QuicPingManagerPeer::GetAlarm(&manager_))) {
clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1));
}
protected:
testing::StrictMock<MockDelegate> delegate_;
MockClock clock_;
QuicConnectionArena arena_;
MockAlarmFactory alarm_factory_;
QuicPingManager manager_;
MockAlarmFactory::TestAlarm* alarm_;
};
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);
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);
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);
EXPECT_TRUE(alarm_->IsSet());
// Verify alarm is not armed if !kShouldKeepAlive.
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
manager_.SetAlarm(clock_.ApproximateNow(), !kShouldKeepAlive,
kHasInflightPackets);
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);
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);
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);
EXPECT_TRUE(alarm_->IsSet());
// Verify alarm is not armed if !kShouldKeepAlive.
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
manager_.SetAlarm(clock_.ApproximateNow(), !kShouldKeepAlive,
kHasInflightPackets);
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);
// 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);
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);
// 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);
// 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);
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);
}
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);
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);
}
// 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);
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);
// 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);
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);
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);
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);
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);
// Advance 5ms to receive next packet.
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
}
manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
!kHasInflightPackets);
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);
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);
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);
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);
}
manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
!kHasInflightPackets);
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);
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);
}
for (int i = 1; i <= 20; ++i) {
manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
!kHasInflightPackets);
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();
}
}
} // namespace
} // namespace test
} // namespace quic