1
0
mirror of https://github.com/facebookincubator/mvfst.git synced 2025-08-09 20:42:44 +03:00
Files
mvfst/quic/d6d/test/QuicD6DStateFunctionsTest.cpp
Sridhar Srinivasan 7246cd5e3c Record stream details in packet metadata
Summary:
Introruced a new DetailsPerStream struct inside the outstanding packet metadata
so we can easily track all streams carrying app data within a packet and each
stream's bytes sent (both new and total).

With this, consumers can easily deduce the number of retransmitted bytes per
stream using the StreamDetails members:
newStreamBytesSent - streamBytesSent.

Reviewed By: bschlinker

Differential Revision: D28063916

fbshipit-source-id: d915514f85f24f4889cfac2f7a66bfd2d6b0a4af
2021-05-14 11:55:17 -07:00

693 lines
24 KiB
C++

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <quic/api/test/Mocks.h>
#include <quic/common/test/TestUtils.h>
#include <quic/d6d/QuicD6DStateFunctions.h>
#include <quic/d6d/test/Mocks.h>
#include <quic/state/StateData.h>
using namespace testing;
using namespace std::literals::chrono_literals;
namespace quic {
namespace test {
// timeLastNonSearchState can only increase
enum class TimeLastNonSearchStateEnd : uint8_t { EQ, GE };
struct D6DProbeLostTestFixture {
D6DMachineState stateBegin;
D6DMachineState stateEnd;
bool sendProbeBegin;
folly::Optional<std::chrono::milliseconds> sendProbeDelayEnd;
// probe loss doesn't change outstanding probes, so a begin value
// is enough
uint64_t outstandingProbes;
uint32_t currentProbeSizeBegin;
uint32_t currentProbeSizeEnd;
D6DProbePacket lastProbe;
D6DMachineState lastNonSearchStateBegin;
D6DMachineState lastNonSearchStateEnd;
TimePoint timeLastNonSearchStateBegin;
TimeLastNonSearchStateEnd timeLastNonSearchStateEndE;
};
RegularQuicWritePacket makeTestShortPacket() {
ShortHeader header(
ProtectionType::KeyPhaseZero, getTestConnectionId(), 2 /* packetNum */);
RegularQuicWritePacket packet(std::move(header));
return packet;
}
class QuicD6DStateFunctionsTest : public Test {
public:
void runD6DProbeLostTest(
QuicConnectionStateBase& conn,
D6DProbeLostTestFixture fixture) {
conn.d6d.state = fixture.stateBegin;
conn.d6d.outstandingProbes = fixture.outstandingProbes;
conn.d6d.currentProbeSize = fixture.currentProbeSizeBegin;
conn.d6d.lastProbe = fixture.lastProbe;
conn.pendingEvents.d6d.sendProbePacket = fixture.sendProbeBegin;
conn.d6d.meta.lastNonSearchState = fixture.lastNonSearchStateBegin;
conn.d6d.meta.timeLastNonSearchState = fixture.timeLastNonSearchStateBegin;
onD6DLastProbeLost(conn);
EXPECT_EQ(conn.d6d.state, fixture.stateEnd);
EXPECT_EQ(conn.d6d.currentProbeSize, fixture.currentProbeSizeEnd);
if (fixture.sendProbeDelayEnd.hasValue()) {
ASSERT_TRUE(conn.pendingEvents.d6d.sendProbeDelay.hasValue());
EXPECT_EQ(
*conn.pendingEvents.d6d.sendProbeDelay, *fixture.sendProbeDelayEnd);
} else {
ASSERT_FALSE(conn.pendingEvents.d6d.sendProbeDelay.hasValue());
}
EXPECT_EQ(conn.d6d.meta.lastNonSearchState, fixture.lastNonSearchStateEnd);
switch (fixture.timeLastNonSearchStateEndE) {
case TimeLastNonSearchStateEnd::EQ:
EXPECT_EQ(
conn.d6d.meta.timeLastNonSearchState,
fixture.timeLastNonSearchStateBegin);
break;
default:
EXPECT_GE(
conn.d6d.meta.timeLastNonSearchState,
fixture.timeLastNonSearchStateBegin);
}
}
};
TEST_F(QuicD6DStateFunctionsTest, D6DProbeTimeoutExpiredOneInBase) {
QuicConnectionStateBase conn(QuicNodeType::Server);
auto now = Clock::now();
// One probe lost in BASE state
D6DProbeLostTestFixture oneProbeLostInBase = {
D6DMachineState::BASE, // stateBegin
D6DMachineState::BASE, // stateEnd
false, // sendProbeBegin
kDefaultD6DProbeDelayWhenLost, // sendProbeEnd
1, // outstandingProbes
conn.d6d.basePMTU, // currentProbeSizeBegin
conn.d6d.basePMTU, // currentProbeSizeEnd
D6DProbePacket(0, conn.d6d.basePMTU + 10),
D6DMachineState::DISABLED, // lastNonSearchStateBegin
D6DMachineState::DISABLED, // lastNonSearchStateEnd
now, // timeLastNonSearchStateBegin
TimeLastNonSearchStateEnd::EQ // timeLastNonSearchStateEndE
};
runD6DProbeLostTest(conn, oneProbeLostInBase);
}
TEST_F(QuicD6DStateFunctionsTest, D6DProbeTimeoutExpiredMaxInBase) {
QuicConnectionStateBase conn(QuicNodeType::Server);
auto now = Clock::now();
// max number of probes lost in BASE state
D6DProbeLostTestFixture maxNumProbesLostInBase = {
D6DMachineState::BASE,
D6DMachineState::ERROR,
false,
kDefaultD6DProbeDelayWhenLost,
kDefaultD6DMaxOutstandingProbes,
conn.d6d.basePMTU,
kMinMaxUDPPayload,
D6DProbePacket(0, conn.d6d.basePMTU + 10),
D6DMachineState::DISABLED,
D6DMachineState::BASE,
now,
TimeLastNonSearchStateEnd::GE};
runD6DProbeLostTest(conn, maxNumProbesLostInBase);
}
TEST_F(QuicD6DStateFunctionsTest, D6DProbeTimeoutExpiredOneInSearching) {
QuicConnectionStateBase conn(QuicNodeType::Server);
auto now = Clock::now();
// One probe lots in SEARCHING state
D6DProbeLostTestFixture oneProbeLostInSearching = {
D6DMachineState::SEARCHING,
D6DMachineState::SEARCHING,
false,
kDefaultD6DProbeDelayWhenLost,
1,
static_cast<uint32_t>(conn.d6d.basePMTU + 10),
static_cast<uint32_t>(conn.d6d.basePMTU + 10),
D6DProbePacket(0, conn.d6d.basePMTU + 10),
D6DMachineState::BASE,
D6DMachineState::BASE,
now,
TimeLastNonSearchStateEnd::EQ};
runD6DProbeLostTest(conn, oneProbeLostInSearching);
}
TEST_F(QuicD6DStateFunctionsTest, D6DProbeTimeoutExpiredMaxInSearching) {
QuicConnectionStateBase conn(QuicNodeType::Server);
auto now = Clock::now();
// Max number of probes lost in SEARCHING state
D6DProbeLostTestFixture maxProbesLostInSearching = {
D6DMachineState::SEARCHING,
D6DMachineState::SEARCH_COMPLETE,
false,
folly::none,
kDefaultD6DMaxOutstandingProbes,
static_cast<uint32_t>(conn.d6d.basePMTU + 10),
static_cast<uint32_t>(conn.d6d.basePMTU + 10),
D6DProbePacket(0, conn.d6d.basePMTU + 10),
D6DMachineState::BASE,
D6DMachineState::BASE,
now,
TimeLastNonSearchStateEnd::EQ};
runD6DProbeLostTest(conn, maxProbesLostInSearching);
}
TEST_F(QuicD6DStateFunctionsTest, D6DProbeTimeoutExpiredOneInError) {
QuicConnectionStateBase conn(QuicNodeType::Server);
auto now = Clock::now();
// Probe lost in ERROR state
D6DProbeLostTestFixture probeLostInError = {
D6DMachineState::ERROR,
D6DMachineState::ERROR,
false,
kDefaultD6DProbeDelayWhenLost,
kDefaultD6DMaxOutstandingProbes + 1,
kMinMaxUDPPayload,
kMinMaxUDPPayload,
D6DProbePacket(0, conn.d6d.basePMTU + 10),
D6DMachineState::BASE,
D6DMachineState::BASE,
now,
TimeLastNonSearchStateEnd::EQ};
runD6DProbeLostTest(conn, probeLostInError);
}
TEST_F(QuicD6DStateFunctionsTest, D6DProbeAckedInBase) {
QuicConnectionStateBase conn(QuicNodeType::Server);
const uint16_t expectPMTU = 1400;
auto& d6d = conn.d6d;
auto now = Clock::now();
Observer::Config config = {};
config.pmtuEvents = true;
auto mockObserver = std::make_unique<StrictMock<MockObserver>>(config);
auto observers = std::make_shared<ObserverVec>();
observers->emplace_back(mockObserver.get());
conn.observers = observers;
d6d.state = D6DMachineState::BASE;
d6d.outstandingProbes = 1;
d6d.currentProbeSize = d6d.basePMTU;
d6d.meta.lastNonSearchState = D6DMachineState::DISABLED;
d6d.meta.timeLastNonSearchState = now;
auto pkt = OutstandingPacket(
makeTestShortPacket(),
Clock::now(),
d6d.currentProbeSize,
0,
false,
true,
d6d.currentProbeSize,
d6d.currentProbeSize,
0,
LossState(),
0);
d6d.lastProbe = D6DProbePacket(
pkt.packet.header.getPacketSequenceNum(), pkt.metadata.encodedSize);
d6d.raiser = std::make_unique<MockProbeSizeRaiser>();
auto mockRaiser = dynamic_cast<MockProbeSizeRaiser*>(d6d.raiser.get());
EXPECT_CALL(*mockRaiser, raiseProbeSize(d6d.currentProbeSize))
.Times(1)
.WillOnce(Return(expectPMTU));
EXPECT_CALL(*mockObserver, pmtuUpperBoundDetected(_, _)).Times(0);
onD6DLastProbeAcked(conn);
for (auto& callback : conn.pendingCallbacks) {
callback(nullptr);
}
EXPECT_EQ(d6d.state, D6DMachineState::SEARCHING);
EXPECT_EQ(d6d.currentProbeSize, expectPMTU);
EXPECT_EQ(conn.udpSendPacketLen, d6d.basePMTU);
EXPECT_EQ(d6d.meta.lastNonSearchState, D6DMachineState::BASE);
EXPECT_GE(d6d.meta.timeLastNonSearchState, now);
}
TEST_F(QuicD6DStateFunctionsTest, D6DProbeAckedInSearchingOne) {
QuicConnectionStateBase conn(QuicNodeType::Server);
const uint16_t expectPMTU = 1400;
auto& d6d = conn.d6d;
auto now = Clock::now();
Observer::Config config = {};
config.pmtuEvents = true;
auto mockObserver = std::make_unique<StrictMock<MockObserver>>(config);
auto observers = std::make_shared<ObserverVec>();
observers->emplace_back(mockObserver.get());
conn.observers = observers;
d6d.state = D6DMachineState::SEARCHING;
d6d.outstandingProbes = 1;
conn.udpSendPacketLen = 1250;
d6d.currentProbeSize = 1300;
d6d.meta.lastNonSearchState = D6DMachineState::BASE;
d6d.meta.timeLastNonSearchState = now;
auto pkt = OutstandingPacket(
makeTestShortPacket(),
Clock::now(),
d6d.currentProbeSize,
0,
false,
true,
d6d.currentProbeSize,
d6d.currentProbeSize,
0,
LossState(),
0);
d6d.lastProbe = D6DProbePacket(
pkt.packet.header.getPacketSequenceNum(), pkt.metadata.encodedSize);
d6d.raiser = std::make_unique<MockProbeSizeRaiser>();
auto mockRaiser = dynamic_cast<MockProbeSizeRaiser*>(d6d.raiser.get());
EXPECT_CALL(*mockRaiser, raiseProbeSize(d6d.currentProbeSize))
.Times(1)
.WillOnce(Return(expectPMTU));
EXPECT_CALL(*mockObserver, pmtuUpperBoundDetected(_, _)).Times(0);
onD6DLastProbeAcked(conn);
for (auto& callback : conn.pendingCallbacks) {
callback(nullptr);
}
EXPECT_EQ(d6d.state, D6DMachineState::SEARCHING);
EXPECT_EQ(d6d.currentProbeSize, expectPMTU);
EXPECT_EQ(conn.udpSendPacketLen, 1300);
EXPECT_EQ(d6d.meta.lastNonSearchState, D6DMachineState::BASE);
EXPECT_EQ(d6d.meta.timeLastNonSearchState, now);
}
TEST_F(QuicD6DStateFunctionsTest, D6DProbeAckedInSearchingMax) {
QuicConnectionStateBase conn(QuicNodeType::Server);
const uint16_t oversize = 1500;
auto& d6d = conn.d6d;
auto now = Clock::now();
Observer::Config config = {};
config.pmtuEvents = true;
auto mockObserver = std::make_unique<StrictMock<MockObserver>>(config);
auto observers = std::make_shared<ObserverVec>();
observers->emplace_back(mockObserver.get());
conn.observers = observers;
d6d.state = D6DMachineState::SEARCHING;
d6d.outstandingProbes = 3;
conn.udpSendPacketLen = 1400;
d6d.currentProbeSize = 1450;
d6d.meta.lastNonSearchState = D6DMachineState::BASE;
d6d.meta.timeLastNonSearchState = now;
d6d.meta.totalTxedProbes = 10;
auto pkt = OutstandingPacket(
makeTestShortPacket(),
Clock::now(),
d6d.currentProbeSize,
0,
false,
true,
d6d.currentProbeSize,
d6d.currentProbeSize,
0,
LossState(),
0);
d6d.lastProbe = D6DProbePacket(
pkt.packet.header.getPacketSequenceNum(), pkt.metadata.encodedSize);
d6d.raiser = std::make_unique<MockProbeSizeRaiser>();
auto mockRaiser = dynamic_cast<MockProbeSizeRaiser*>(d6d.raiser.get());
EXPECT_CALL(*mockRaiser, raiseProbeSize(d6d.currentProbeSize))
.Times(1)
.WillOnce(Return(oversize));
EXPECT_CALL(*mockObserver, pmtuUpperBoundDetected(_, _))
.Times(1)
.WillOnce(Invoke([&](QuicSocket* /* qSocket */,
const Observer::PMTUUpperBoundEvent& event) {
EXPECT_LT(now, event.upperBoundTime);
EXPECT_LT(0us, event.timeSinceLastNonSearchState);
EXPECT_EQ(D6DMachineState::BASE, event.lastNonSearchState);
EXPECT_EQ(1450, event.upperBoundPMTU);
EXPECT_EQ(10, event.cumulativeProbesSent);
EXPECT_EQ(ProbeSizeRaiserType::ConstantStep, event.probeSizeRaiserType);
}));
onD6DLastProbeAcked(conn);
for (auto& callback : conn.pendingCallbacks) {
callback(nullptr);
}
EXPECT_EQ(d6d.state, D6DMachineState::SEARCH_COMPLETE);
EXPECT_EQ(d6d.currentProbeSize, 1450);
EXPECT_EQ(conn.udpSendPacketLen, 1450);
EXPECT_EQ(d6d.meta.lastNonSearchState, D6DMachineState::BASE);
EXPECT_EQ(d6d.meta.timeLastNonSearchState, now);
}
TEST_F(QuicD6DStateFunctionsTest, D6DProbeAckedInError) {
QuicConnectionStateBase conn(QuicNodeType::Server);
auto& d6d = conn.d6d;
auto now = Clock::now();
Observer::Config config = {};
config.pmtuEvents = true;
auto mockObserver = std::make_unique<StrictMock<MockObserver>>(config);
auto observers = std::make_shared<ObserverVec>();
observers->emplace_back(mockObserver.get());
conn.observers = observers;
d6d.state = D6DMachineState::ERROR;
d6d.outstandingProbes = 3;
conn.udpSendPacketLen = d6d.basePMTU;
d6d.currentProbeSize = d6d.basePMTU - 20;
d6d.meta.lastNonSearchState = D6DMachineState::BASE;
d6d.meta.timeLastNonSearchState = now;
auto pkt = OutstandingPacket(
makeTestShortPacket(),
Clock::now(),
d6d.currentProbeSize,
0,
false,
true,
d6d.currentProbeSize,
d6d.currentProbeSize,
0,
LossState(),
0);
d6d.lastProbe = D6DProbePacket(
pkt.packet.header.getPacketSequenceNum(), pkt.metadata.encodedSize);
d6d.raiser = std::make_unique<MockProbeSizeRaiser>();
auto mockRaiser = dynamic_cast<MockProbeSizeRaiser*>(d6d.raiser.get());
EXPECT_CALL(*mockRaiser, raiseProbeSize(d6d.currentProbeSize))
.Times(1)
.WillOnce(Return(1300)); // Won't be used
EXPECT_CALL(*mockObserver, pmtuUpperBoundDetected(_, _)).Times(0);
onD6DLastProbeAcked(conn);
for (auto& callback : conn.pendingCallbacks) {
callback(nullptr);
}
EXPECT_EQ(d6d.state, D6DMachineState::BASE);
EXPECT_EQ(d6d.currentProbeSize, d6d.basePMTU);
EXPECT_EQ(conn.udpSendPacketLen, d6d.basePMTU);
EXPECT_EQ(d6d.meta.lastNonSearchState, D6DMachineState::ERROR);
EXPECT_GE(d6d.meta.timeLastNonSearchState, now);
}
TEST_F(QuicD6DStateFunctionsTest, BlackholeInSearching) {
QuicConnectionStateBase conn(QuicNodeType::Server);
auto& d6d = conn.d6d;
auto now = Clock::now();
Observer::Config config = {};
config.pmtuEvents = true;
auto mockObserver = std::make_unique<StrictMock<MockObserver>>(config);
auto observers = std::make_shared<ObserverVec>();
observers->emplace_back(mockObserver.get());
conn.observers = observers;
d6d.state = D6DMachineState::SEARCHING;
d6d.outstandingProbes = 2;
conn.udpSendPacketLen = d6d.basePMTU + 20;
d6d.currentProbeSize = d6d.basePMTU + 30;
d6d.meta.lastNonSearchState = D6DMachineState::BASE;
d6d.meta.timeLastNonSearchState = now;
auto pkt = OutstandingPacket(
makeTestShortPacket(),
now + 10s,
d6d.currentProbeSize,
0,
false,
true,
d6d.currentProbeSize,
d6d.currentProbeSize,
0,
LossState(),
0);
d6d.lastProbe = D6DProbePacket(
pkt.packet.header.getPacketSequenceNum(), pkt.metadata.encodedSize);
auto lostPacket = OutstandingPacket(
makeTestShortPacket(),
now + 8s,
conn.udpSendPacketLen,
0,
false,
conn.udpSendPacketLen + d6d.currentProbeSize,
0,
conn.udpSendPacketLen + d6d.currentProbeSize,
0,
LossState(),
0);
d6d.thresholdCounter = std::make_unique<WindowedCounter<uint64_t, uint64_t>>(
std::chrono::microseconds(kDefaultD6DBlackholeDetectionWindow).count(),
1); // Threshold of 1 will cause window to be set to 0
EXPECT_CALL(*mockObserver, pmtuBlackholeDetected(_, _))
.Times(1)
.WillOnce(Invoke([&](QuicSocket* /* qSocket */,
const Observer::PMTUBlackholeEvent& event) {
EXPECT_LE(d6d.meta.timeLastNonSearchState, event.blackholeTime);
EXPECT_EQ(D6DMachineState::BASE, event.lastNonSearchState);
EXPECT_EQ(D6DMachineState::SEARCHING, event.currentState);
EXPECT_EQ(d6d.basePMTU + 20, event.udpSendPacketLen);
EXPECT_EQ(d6d.basePMTU + 30, event.lastProbeSize);
EXPECT_EQ(0, event.blackholeDetectionWindow);
EXPECT_EQ(1, event.blackholeDetectionThreshold);
EXPECT_EQ(
d6d.basePMTU + 20, event.triggeringPacketMetadata.encodedSize);
}));
detectPMTUBlackhole(conn, lostPacket);
for (auto& callback : conn.pendingCallbacks) {
callback(nullptr);
}
EXPECT_EQ(d6d.state, D6DMachineState::BASE);
EXPECT_EQ(d6d.currentProbeSize, d6d.basePMTU);
EXPECT_EQ(conn.udpSendPacketLen, d6d.basePMTU);
EXPECT_EQ(d6d.meta.lastNonSearchState, D6DMachineState::BASE);
EXPECT_GE(d6d.meta.timeLastNonSearchState, now);
}
TEST_F(QuicD6DStateFunctionsTest, BlackholeInSearchComplete) {
QuicConnectionStateBase conn(QuicNodeType::Server);
auto& d6d = conn.d6d;
auto now = Clock::now();
Observer::Config config = {};
config.pmtuEvents = true;
auto mockObserver = std::make_unique<StrictMock<MockObserver>>(config);
auto observers = std::make_shared<ObserverVec>();
observers->emplace_back(mockObserver.get());
conn.observers = observers;
d6d.state = D6DMachineState::SEARCH_COMPLETE;
conn.udpSendPacketLen = d6d.basePMTU + 20;
d6d.currentProbeSize = d6d.basePMTU + 20;
d6d.meta.lastNonSearchState = D6DMachineState::BASE;
d6d.meta.timeLastNonSearchState = now;
auto pkt = OutstandingPacket(
makeTestShortPacket(),
now + 10s,
d6d.currentProbeSize,
0,
false,
true,
d6d.currentProbeSize,
d6d.currentProbeSize,
0,
LossState(),
0);
d6d.lastProbe = D6DProbePacket(
pkt.packet.header.getPacketSequenceNum(), pkt.metadata.encodedSize);
auto lostPacket = OutstandingPacket(
makeTestShortPacket(),
now + 12s,
conn.udpSendPacketLen,
0,
false,
conn.udpSendPacketLen + d6d.currentProbeSize,
0,
conn.udpSendPacketLen + d6d.currentProbeSize,
0,
LossState(),
0);
d6d.thresholdCounter = std::make_unique<WindowedCounter<uint64_t, uint64_t>>(
std::chrono::microseconds(kDefaultD6DBlackholeDetectionWindow).count(),
1); // Threshold of 1 will cause window to be set to 0
EXPECT_CALL(*mockObserver, pmtuBlackholeDetected(_, _))
.Times(1)
.WillOnce(Invoke([&](QuicSocket* /* qSocket */,
const Observer::PMTUBlackholeEvent& event) {
EXPECT_EQ(d6d.meta.timeLastNonSearchState, event.blackholeTime);
EXPECT_EQ(D6DMachineState::BASE, event.lastNonSearchState);
EXPECT_EQ(D6DMachineState::SEARCH_COMPLETE, event.currentState);
EXPECT_EQ(d6d.basePMTU + 20, event.udpSendPacketLen);
EXPECT_EQ(d6d.basePMTU + 20, event.lastProbeSize);
EXPECT_EQ(0, event.blackholeDetectionWindow);
EXPECT_EQ(1, event.blackholeDetectionThreshold);
EXPECT_EQ(
d6d.basePMTU + 20, event.triggeringPacketMetadata.encodedSize);
}));
detectPMTUBlackhole(conn, lostPacket);
for (auto& callback : conn.pendingCallbacks) {
callback(nullptr);
}
EXPECT_EQ(d6d.state, D6DMachineState::BASE);
EXPECT_EQ(d6d.currentProbeSize, d6d.basePMTU);
EXPECT_EQ(conn.udpSendPacketLen, d6d.basePMTU);
EXPECT_EQ(d6d.meta.lastNonSearchState, D6DMachineState::SEARCH_COMPLETE);
EXPECT_GE(d6d.meta.timeLastNonSearchState, now);
}
TEST_F(QuicD6DStateFunctionsTest, ReachMaxPMTU) {
QuicConnectionStateBase conn(QuicNodeType::Server);
auto& d6d = conn.d6d;
auto now = Clock::now();
Observer::Config config = {};
config.pmtuEvents = true;
auto mockObserver = std::make_unique<StrictMock<MockObserver>>(config);
auto observers = std::make_shared<ObserverVec>();
observers->emplace_back(mockObserver.get());
conn.observers = observers;
d6d.state = D6DMachineState::SEARCHING;
d6d.maxPMTU = 1452;
d6d.outstandingProbes = 1;
conn.udpSendPacketLen = 1400;
d6d.currentProbeSize = 1442;
d6d.meta.lastNonSearchState = D6DMachineState::BASE;
d6d.meta.timeLastNonSearchState = now;
d6d.meta.totalTxedProbes = 10;
auto pkt = OutstandingPacket(
makeTestShortPacket(),
Clock::now(),
d6d.currentProbeSize,
0,
false,
true,
d6d.currentProbeSize,
d6d.currentProbeSize,
0,
LossState(),
0);
d6d.lastProbe = D6DProbePacket(
pkt.packet.header.getPacketSequenceNum(), pkt.metadata.encodedSize);
d6d.raiser = std::make_unique<MockProbeSizeRaiser>();
auto mockRaiser = dynamic_cast<MockProbeSizeRaiser*>(d6d.raiser.get());
EXPECT_CALL(*mockRaiser, raiseProbeSize(d6d.currentProbeSize))
.Times(1)
.WillOnce(Return(1452));
onD6DLastProbeAcked(conn);
EXPECT_EQ(d6d.state, D6DMachineState::SEARCHING);
EXPECT_EQ(d6d.currentProbeSize, 1452);
EXPECT_EQ(conn.udpSendPacketLen, 1442);
EXPECT_EQ(d6d.meta.lastNonSearchState, D6DMachineState::BASE);
EXPECT_EQ(d6d.meta.timeLastNonSearchState, now);
}
TEST_F(
QuicD6DStateFunctionsTest,
MaintainStateWhenFalsePositiveBlackholeDetected) {
QuicConnectionStateBase conn(QuicNodeType::Server);
auto& d6d = conn.d6d;
auto now = Clock::now();
d6d.state = D6DMachineState::SEARCHING;
d6d.maxPMTU = 1452;
d6d.outstandingProbes = 1;
conn.udpSendPacketLen = 1400;
d6d.currentProbeSize = 1442;
d6d.meta.lastNonSearchState = D6DMachineState::BASE;
d6d.meta.timeLastNonSearchState = now;
d6d.meta.totalTxedProbes = 10;
auto pkt = OutstandingPacket(
makeTestShortPacket(),
Clock::now(),
d6d.currentProbeSize,
0,
false,
true,
d6d.currentProbeSize,
d6d.currentProbeSize,
0,
LossState(),
0);
d6d.lastProbe = D6DProbePacket(
pkt.packet.header.getPacketSequenceNum(), pkt.metadata.encodedSize);
d6d.raiser = std::make_unique<MockProbeSizeRaiser>();
auto mockRaiser = dynamic_cast<MockProbeSizeRaiser*>(d6d.raiser.get());
EXPECT_CALL(*mockRaiser, raiseProbeSize(d6d.currentProbeSize))
.Times(1)
.WillOnce(Return(1452));
d6d.thresholdCounter = std::make_unique<WindowedCounter<uint64_t, uint64_t>>(
std::chrono::microseconds(kDefaultD6DBlackholeDetectionWindow).count(),
1); // Threshold of 1 will cause window to be set to 0
auto lostPacket = OutstandingPacket(
makeTestShortPacket(),
Clock::now(),
d6d.currentProbeSize,
0,
false,
false,
d6d.currentProbeSize,
d6d.currentProbeSize,
0,
LossState(),
0);
// Generate a false positive blackhole signal
detectPMTUBlackhole(conn, lostPacket);
EXPECT_EQ(d6d.state, D6DMachineState::BASE);
EXPECT_EQ(conn.udpSendPacketLen, d6d.basePMTU);
// The ack of a non-stale probe should bring us back to SEARCHING state and
// correct probe size
onD6DLastProbeAcked(conn);
EXPECT_EQ(d6d.state, D6DMachineState::SEARCHING);
EXPECT_EQ(d6d.currentProbeSize, 1452);
EXPECT_EQ(conn.udpSendPacketLen, 1442);
EXPECT_EQ(d6d.meta.lastNonSearchState, D6DMachineState::BASE);
EXPECT_EQ(d6d.meta.timeLastNonSearchState, now);
}
TEST_F(QuicD6DStateFunctionsTest, UpperboundIsBase) {
QuicConnectionStateBase conn(QuicNodeType::Server);
auto& d6d = conn.d6d;
auto now = Clock::now();
d6d.state = D6DMachineState::BASE;
d6d.basePMTU = 1400;
d6d.maxPMTU = 1400;
d6d.outstandingProbes = 1;
conn.udpSendPacketLen = 1400;
d6d.currentProbeSize = 1400;
d6d.meta.lastNonSearchState = D6DMachineState::DISABLED;
d6d.meta.timeLastNonSearchState = now;
d6d.meta.totalTxedProbes = 10;
auto pkt = OutstandingPacket(
makeTestShortPacket(),
Clock::now(),
d6d.currentProbeSize,
0,
false,
true,
d6d.currentProbeSize,
d6d.currentProbeSize,
0,
LossState(),
0);
d6d.lastProbe = D6DProbePacket(
pkt.packet.header.getPacketSequenceNum(), pkt.metadata.encodedSize);
d6d.raiser = std::make_unique<MockProbeSizeRaiser>();
auto mockRaiser = dynamic_cast<MockProbeSizeRaiser*>(d6d.raiser.get());
EXPECT_CALL(*mockRaiser, raiseProbeSize(d6d.currentProbeSize))
.Times(1)
.WillOnce(Return(1452));
// The ack of a non-stale probe should bring us back to SEARCHING state and
// correct probe size
onD6DLastProbeAcked(conn);
EXPECT_EQ(d6d.state, D6DMachineState::SEARCH_COMPLETE);
EXPECT_EQ(d6d.currentProbeSize, 1400);
EXPECT_EQ(conn.udpSendPacketLen, 1400);
EXPECT_EQ(d6d.meta.lastNonSearchState, D6DMachineState::BASE);
EXPECT_GT(d6d.meta.timeLastNonSearchState, now);
}
} // namespace test
} // namespace quic