1
0
mirror of https://github.com/facebookincubator/mvfst.git synced 2025-11-25 15:43:13 +03:00
Files
mvfst/quic/state/test/AckHandlersTest.cpp
Subodh Iyengar 04baa15a04 Custom variant type for packetheader
Summary:
Make a custom variant type for PacketHeader. By not relying on boost::variant
this reduces the code size of the implementation.

This uses a combination of a union type as well as a enum type to emulate a variant

Reviewed By: yangchi

Differential Revision: D17187589

fbshipit-source-id: 00c2b9b8dd3f3e73af766d84888b13b9d867165a
2019-09-19 17:31:47 -07:00

1008 lines
35 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/common/test/TestUtils.h>
#include <quic/server/state/ServerStateMachine.h>
#include <quic/state/AckHandlers.h>
#include <quic/state/StateData.h>
#include <quic/state/test/Mocks.h>
#include <numeric>
using namespace folly;
using namespace testing;
namespace quic {
namespace test {
class AckHandlersTest : public TestWithParam<PacketNumberSpace> {};
auto testLossHandler(std::vector<PacketNum>& lostPackets) -> decltype(auto) {
return [&lostPackets](
QuicConnectionStateBase&, auto& packet, bool, PacketNum) {
auto packetNum = packet.header.getPacketSequenceNum();
lostPackets.push_back(packetNum);
};
}
TEST_P(AckHandlersTest, TestAckMultipleSequentialBlocks) {
QuicServerConnectionState conn;
conn.lossState.reorderingThreshold = 85;
auto mockCongestionController = std::make_unique<MockCongestionController>();
auto rawCongestionController = mockCongestionController.get();
conn.congestionController = std::move(mockCongestionController);
// Get the time based loss detection out of the way
conn.lossState.srtt = 10s;
StreamId current = 10;
auto sentTime = Clock::now();
for (PacketNum packetNum = 10; packetNum <= 101; packetNum++) {
RegularQuicWritePacket regularPacket =
createNewPacket(packetNum, GetParam());
WriteStreamFrame frame(current++, 0, 0, true);
regularPacket.frames.emplace_back(std::move(frame));
conn.outstandingPackets.emplace_back(OutstandingPacket(
std::move(regularPacket), sentTime, 1, false, false, packetNum));
}
ReadAckFrame ackFrame;
ackFrame.largestAcked = 101;
// ACK packet ranges 21 - 101
for (PacketNum packetNum = 101; packetNum > 30; packetNum -= 20) {
ackFrame.ackBlocks.emplace_back(packetNum - 20, packetNum);
}
std::vector<WriteStreamFrame> streams;
std::vector<PacketNum> lostPackets;
uint64_t expectedAckedBytes = 81;
uint64_t expectedAckedPackets = expectedAckedBytes; // each packet size is 1
size_t lostPacketsCounter = 0;
EXPECT_CALL(*rawCongestionController, onPacketAckOrLoss(_, _))
.WillRepeatedly(Invoke([&](auto ack, auto loss) {
if (ack) {
EXPECT_EQ(101, *ack->largestAckedPacket);
EXPECT_EQ(expectedAckedBytes, ack->ackedBytes);
EXPECT_EQ(expectedAckedPackets, ack->ackedPackets.size());
}
if (loss) {
lostPacketsCounter++;
}
}));
processAckFrame(
conn,
GetParam(),
ackFrame,
[&](const auto&, const auto& packetFrame, const ReadAckFrame&) {
auto& stream = boost::get<WriteStreamFrame>(packetFrame);
streams.emplace_back(stream);
},
testLossHandler(lostPackets),
Clock::now());
EXPECT_EQ(lostPacketsCounter, lostPackets.empty() ? 0 : 1);
StreamId start = 21;
for (auto& stream : streams) {
EXPECT_EQ(stream.streamId, start);
start++;
}
// only unacked packets should be remaining
EXPECT_EQ(conn.outstandingPackets.size(), 5);
PacketNum lostPackt = 10;
for (auto& pkt : lostPackets) {
EXPECT_EQ(pkt, lostPackt++);
}
PacketNum packetNum = 16;
for (auto& packet : conn.outstandingPackets) {
auto currentPacketNum = packet.packet.header.getPacketSequenceNum();
EXPECT_EQ(currentPacketNum, packetNum);
packetNum++;
}
}
TEST_P(AckHandlersTest, TestAckBlocksWithGaps) {
QuicServerConnectionState conn;
conn.lossState.reorderingThreshold = 30;
auto mockCongestionController = std::make_unique<MockCongestionController>();
auto rawCongestionController = mockCongestionController.get();
conn.congestionController = std::move(mockCongestionController);
// Get the time based loss detection out of the way
conn.lossState.srtt = 10s;
StreamId current = 10;
for (PacketNum packetNum = 10; packetNum < 51; packetNum++) {
auto regularPacket = createNewPacket(packetNum, GetParam());
WriteStreamFrame frame(current++, 0, 0, true);
regularPacket.frames.emplace_back(std::move(frame));
conn.outstandingPackets.emplace_back(OutstandingPacket(
std::move(regularPacket), Clock::now(), 1, false, false, packetNum));
}
ReadAckFrame ackFrame;
ackFrame.largestAcked = 45;
ackFrame.ackBlocks.emplace_back(45, 45);
ackFrame.ackBlocks.emplace_back(33, 44);
ackFrame.ackBlocks.emplace_back(12, 21);
std::vector<WriteStreamFrame> streams;
std::vector<PacketNum> lostPackets;
uint64_t expectedAckedBytes = 21 - 12 + 1 + 44 - 33 + 1 + 45 - 45 + 1;
uint64_t expectedAckedPackets = expectedAckedBytes; // each packet size is 1
size_t lostPacketsCounter = 0;
EXPECT_CALL(*rawCongestionController, onPacketAckOrLoss(_, _))
.WillRepeatedly(Invoke([&](auto ack, auto loss) {
if (ack) {
EXPECT_EQ(45, *ack->largestAckedPacket);
EXPECT_EQ(expectedAckedBytes, ack->ackedBytes);
EXPECT_EQ(expectedAckedPackets, ack->ackedPackets.size());
}
if (loss) {
lostPacketsCounter++;
}
}));
processAckFrame(
conn,
GetParam(),
ackFrame,
[&](const auto&, const auto& packetFrame, const ReadAckFrame&) {
auto& stream = boost::get<WriteStreamFrame>(packetFrame);
streams.emplace_back(stream);
},
testLossHandler(lostPackets),
Clock::now());
EXPECT_EQ(lostPacketsCounter, lostPackets.empty() ? 0 : 1);
StreamId start = 12;
std::vector<StreamId> ids(10);
std::iota(ids.begin(), ids.end(), start);
EXPECT_TRUE(std::equal(
streams.begin(),
streams.begin() + 10,
ids.begin(),
ids.end(),
[](const auto& frame, auto id) { return frame.streamId == id; }));
std::vector<StreamId> ids2(12);
std::iota(ids2.begin(), ids2.end(), 33);
EXPECT_TRUE(std::equal(
streams.begin() + 10,
streams.begin() + 10 + 12,
ids2.begin(),
ids2.end(),
[](const auto& frame, auto id) { return frame.streamId == id; }));
StreamId stream45 = 45;
EXPECT_EQ((streams.begin() + 10 + 12)->streamId, stream45);
std::vector<PacketNum> remainingPackets(11 + 5);
std::iota(remainingPackets.begin(), remainingPackets.begin() + 11, 22);
std::iota(remainingPackets.begin() + 11, remainingPackets.end(), 46);
std::vector<PacketNum> actualPacketNumbers;
std::transform(
conn.outstandingPackets.begin(),
conn.outstandingPackets.end(),
std::back_insert_iterator<decltype(actualPacketNumbers)>(
actualPacketNumbers),
[](const auto& packet) {
return packet.packet.header.getPacketSequenceNum();
});
EXPECT_TRUE(std::equal(
actualPacketNumbers.begin(),
actualPacketNumbers.end(),
remainingPackets.begin(),
remainingPackets.end()));
std::vector<PacketNum> actualLostPackets = {10, 11};
EXPECT_TRUE(std::equal(
actualLostPackets.begin(),
actualLostPackets.end(),
lostPackets.begin(),
lostPackets.end()));
}
TEST_P(AckHandlersTest, TestNonSequentialPacketNumbers) {
QuicServerConnectionState conn;
conn.lossState.reorderingThreshold = 10;
auto mockCongestionController = std::make_unique<MockCongestionController>();
auto rawCongestionController = mockCongestionController.get();
conn.congestionController = std::move(mockCongestionController);
// Get the time based loss detection out of the way
conn.lossState.srtt = 10s;
StreamId current = 10;
for (PacketNum packetNum = 10; packetNum < 20; packetNum++) {
auto regularPacket = createNewPacket(packetNum, GetParam());
WriteStreamFrame frame(current++, 0, 0, true);
regularPacket.frames.emplace_back(std::move(frame));
conn.outstandingPackets.emplace_back(OutstandingPacket(
std::move(regularPacket), Clock::now(), 1, false, false, packetNum));
}
for (PacketNum packetNum = 20; packetNum < 40; packetNum += 3) {
auto regularPacket = createNewPacket(packetNum, GetParam());
WriteStreamFrame frame(current, 0, 0, true);
current += 3;
regularPacket.frames.emplace_back(std::move(frame));
conn.outstandingPackets.emplace_back(OutstandingPacket(
std::move(regularPacket), Clock::now(), 1, false, false, packetNum));
}
ReadAckFrame ackFrame;
ackFrame.largestAcked = 26;
ackFrame.ackBlocks.emplace_back(26, 26);
// This intentionally acks an unsent packet. When we start enforcing
// unsent packets then disable this.
ackFrame.ackBlocks.emplace_back(5, 20);
std::vector<WriteStreamFrame> streams;
std::vector<PacketNum> lostPackets;
// Only 26 and [10, 20] are acked:
uint64_t expectedAckedBytes = 20 - 10 + 1 + 1;
uint64_t expectedAckedPackets = expectedAckedBytes; // each packet size is 1
EXPECT_CALL(*rawCongestionController, onPacketAckOrLoss(_, _))
.Times(1)
.WillOnce(Invoke([&](auto ackEvent, auto) {
EXPECT_EQ(26, *ackEvent->largestAckedPacket);
EXPECT_EQ(expectedAckedBytes, ackEvent->ackedBytes);
EXPECT_EQ(expectedAckedPackets, ackEvent->ackedPackets.size());
}));
processAckFrame(
conn,
GetParam(),
ackFrame,
[&](const auto&, const auto& packetFrame, const ReadAckFrame&) {
auto& stream = boost::get<WriteStreamFrame>(packetFrame);
streams.emplace_back(stream);
},
testLossHandler(lostPackets),
Clock::now());
StreamId start = 10;
std::vector<StreamId> ids(11);
std::iota(ids.begin(), ids.end(), start);
EXPECT_TRUE(std::equal(
streams.begin(),
streams.begin() + 11,
ids.begin(),
ids.end(),
[](const auto& frame, auto id) { return frame.streamId == id; }));
EXPECT_EQ(streams.begin() + 11 + 1, streams.end());
EXPECT_EQ((streams.begin() + 11)->streamId, 26);
std::vector<PacketNum> remainingPackets(5);
remainingPackets[0] = 23;
int remainingIdx = 1;
for (PacketNum num = 29; num < 40; num += 3) {
remainingPackets[remainingIdx++] = num;
}
std::vector<PacketNum> actualPacketNumbers;
std::transform(
conn.outstandingPackets.begin(),
conn.outstandingPackets.end(),
std::back_insert_iterator<decltype(actualPacketNumbers)>(
actualPacketNumbers),
[](const auto& packet) {
return packet.packet.header.getPacketSequenceNum();
});
EXPECT_TRUE(std::equal(
actualPacketNumbers.begin(),
actualPacketNumbers.end(),
remainingPackets.begin(),
remainingPackets.end()));
}
TEST_P(AckHandlersTest, AckVisitorForAckTest) {
QuicServerConnectionState conn;
conn.connectionTime = Clock::now();
auto firstPacket = createNewPacket(100 /* packetNum */, GetParam());
WriteAckFrame firstAckFrame;
firstAckFrame.ackBlocks.insert(900, 1000);
firstAckFrame.ackBlocks.insert(500, 700);
conn.ackStates.appDataAckState.acks.insert(900, 1000);
conn.ackStates.appDataAckState.acks.insert(500, 700);
firstPacket.frames.emplace_back(std::move(firstAckFrame));
conn.outstandingPackets.emplace_back(OutstandingPacket(
std::move(firstPacket), Clock::now(), 0, false, false, 0));
auto secondPacket = createNewPacket(101 /* packetNum */, GetParam());
WriteAckFrame secondAckFrame;
secondAckFrame.ackBlocks.insert(1100, 2000);
secondAckFrame.ackBlocks.insert(1002, 1090);
conn.ackStates.appDataAckState.acks.insert(1100, 2000);
conn.ackStates.appDataAckState.acks.insert(1002, 1090);
secondPacket.frames.emplace_back(std::move(secondAckFrame));
conn.outstandingPackets.emplace_back(OutstandingPacket(
std::move(secondPacket), Clock::now(), 0, false, false, 0));
ReadAckFrame firstReceivedAck;
firstReceivedAck.largestAcked = 100;
firstReceivedAck.ackBlocks.emplace_back(100, 100);
processAckFrame(
conn,
GetParam(),
firstReceivedAck,
[&](const auto& outstandingPacket,
const auto& packetFrame,
const ReadAckFrame&) {
auto ackedPacketNum =
outstandingPacket.packet.header.getPacketSequenceNum();
EXPECT_EQ(ackedPacketNum, firstReceivedAck.largestAcked);
folly::variant_match(
packetFrame,
[&](const WriteAckFrame& frame) {
commonAckVisitorForAckFrame(
conn.ackStates.appDataAckState, frame);
},
[&](const auto& /* frame */) {
// Ignore other frames.
});
},
[](auto& /* conn */,
auto& /* packet */,
bool /* processed */,
PacketNum /* currentPacketNum */) {},
Clock::now());
EXPECT_EQ(2, conn.ackStates.appDataAckState.acks.size());
EXPECT_EQ(
Interval<PacketNum>(1002, 1090),
conn.ackStates.appDataAckState.acks.front());
EXPECT_EQ(
Interval<PacketNum>(1100, 2000),
conn.ackStates.appDataAckState.acks.back());
ReadAckFrame secondReceivedAck;
secondReceivedAck.largestAcked = 101;
secondReceivedAck.ackBlocks.emplace_back(101, 101);
processAckFrame(
conn,
GetParam(),
secondReceivedAck,
[&](const auto&, const auto& packetFrame, const ReadAckFrame&) {
folly::variant_match(
packetFrame,
[&](const WriteAckFrame& frame) {
commonAckVisitorForAckFrame(
conn.ackStates.appDataAckState, frame);
},
[&](const auto& /* frame */) {
// Ignore other frames.
});
},
[](auto& /* conn */,
auto& /* packet */,
bool /* processed */,
PacketNum /* currentPacketNum */) {},
Clock::now());
EXPECT_TRUE(conn.ackStates.appDataAckState.acks.empty());
}
TEST_P(AckHandlersTest, NoNewAckedPacket) {
QuicServerConnectionState conn;
auto mockController = std::make_unique<MockCongestionController>();
auto rawController = mockController.get();
conn.congestionController = std::move(mockController);
conn.lossState.ptoCount = 1;
PacketNum packetAfterRtoNum = 10;
auto packetAfterRto = createNewPacket(packetAfterRtoNum, GetParam());
conn.outstandingPackets.emplace_back(OutstandingPacket(
std::move(packetAfterRto), Clock::now(), 0, false, false, 0));
ReadAckFrame ackFrame;
ackFrame.largestAcked = 5;
EXPECT_CALL(*rawController, onPacketAckOrLoss(_, _)).Times(0);
processAckFrame(
conn,
GetParam(),
ackFrame,
[](const auto&, const auto&, const auto&) {},
[](auto&, auto&, bool, PacketNum) {},
Clock::now());
EXPECT_TRUE(conn.pendingEvents.setLossDetectionAlarm);
EXPECT_EQ(conn.lossState.ptoCount, 1);
EXPECT_EQ(conn.ackStates.appDataAckState.largestAckedByPeer, 0);
}
TEST_P(AckHandlersTest, LossByAckedRecovered) {
QuicServerConnectionState conn;
auto mockController = std::make_unique<MockCongestionController>();
conn.congestionController = std::move(mockController);
ReadAckFrame ackFrame;
ackFrame.largestAcked = 10;
ackFrame.ackBlocks.emplace_back(5, 10);
processAckFrame(
conn,
GetParam(),
ackFrame,
[](const auto&, const auto&, const auto&) {},
[](auto&, auto&, bool, PacketNum) {},
Clock::now());
}
TEST_P(AckHandlersTest, AckPacketNumDoesNotExist) {
QuicServerConnectionState conn;
auto mockController = std::make_unique<MockCongestionController>();
conn.congestionController = std::move(mockController);
// Get the time based loss detection out of the way
conn.lossState.srtt = 10s;
PacketNum packetNum1 = 9;
auto regularPacket1 = createNewPacket(packetNum1, GetParam());
conn.outstandingPackets.emplace_back(
std::move(regularPacket1), Clock::now(), 0, false, false, 0);
PacketNum packetNum2 = 10;
auto regularPacket2 = createNewPacket(packetNum2, GetParam());
conn.outstandingPackets.emplace_back(
std::move(regularPacket2), Clock::now(), 0, false, false, 0);
// Ack a packet one higher than the packet so that we don't trigger reordering
// threshold.
ReadAckFrame ackFrame;
ackFrame.largestAcked = 1000;
ackFrame.ackBlocks.emplace_back(1000, 1000);
ackFrame.ackBlocks.emplace_back(10, 10);
processAckFrame(
conn,
GetParam(),
ackFrame,
[](const auto&, const auto&, const auto&) {},
[](auto&, auto&, bool, PacketNum) {},
Clock::now());
EXPECT_EQ(1, conn.outstandingPackets.size());
}
TEST_P(AckHandlersTest, TestHandshakeCounterUpdate) {
QuicServerConnectionState conn;
StreamId stream = 1;
for (PacketNum packetNum = 0; packetNum < 10; packetNum++) {
auto regularPacket = createNewPacket(packetNum, GetParam());
WriteStreamFrame frame(
stream, 100 * packetNum + 0, 100 * packetNum + 100, false);
regularPacket.frames.emplace_back(std::move(frame));
conn.outstandingPackets.emplace_back(
std::move(regularPacket),
Clock::now(),
0,
packetNum % 2,
false,
packetNum / 2);
conn.outstandingHandshakePacketsCount += packetNum % 2;
}
ReadAckFrame ackFrame;
ackFrame.largestAcked = 9;
ackFrame.ackBlocks.emplace_back(3, 7);
std::vector<PacketNum> lostPackets;
processAckFrame(
conn,
GetParam(),
ackFrame,
[&](const auto&, const auto&, const ReadAckFrame&) {},
testLossHandler(lostPackets),
Clock::now());
// When [3, 7] are acked, [0, 2] will also be marked loss, due to reordering
// threshold
EXPECT_EQ(1, conn.outstandingHandshakePacketsCount);
EXPECT_EQ(2, conn.outstandingPackets.size());
}
TEST_P(AckHandlersTest, PurgeAcks) {
QuicServerConnectionState conn;
WriteAckFrame ackFrame;
ackFrame.ackBlocks.insert(900, 1000);
ackFrame.ackBlocks.insert(500, 700);
conn.ackStates.initialAckState.acks.insert(900, 1200);
conn.ackStates.initialAckState.acks.insert(500, 800);
auto expectedTime = Clock::now();
conn.ackStates.initialAckState.largestRecvdPacketTime = expectedTime;
commonAckVisitorForAckFrame(conn.ackStates.initialAckState, ackFrame);
// We should have purged old packets in ack state
EXPECT_EQ(conn.ackStates.initialAckState.acks.size(), 1);
EXPECT_EQ(conn.ackStates.initialAckState.acks.front().start, 1001);
EXPECT_EQ(conn.ackStates.initialAckState.acks.front().end, 1200);
EXPECT_EQ(
expectedTime, *conn.ackStates.initialAckState.largestRecvdPacketTime);
}
TEST_P(AckHandlersTest, PureAckBytesCountedTowardsTotalBytesAcked) {
QuicServerConnectionState conn;
ASSERT_EQ(0, conn.lossState.totalBytesAcked);
auto regularPacket = createNewPacket(10, GetParam());
WriteAckFrame ack;
ack.ackBlocks.insert(2, 5);
conn.outstandingPackets.emplace_back(OutstandingPacket(
std::move(regularPacket), Clock::now(), 2, false, true, 2));
conn.outstandingPureAckPacketsCount++;
ReadAckFrame ackFrame;
ackFrame.largestAcked = 12;
ackFrame.ackBlocks.emplace_back(5, 12);
std::vector<PacketNum> lostPackets;
processAckFrame(
conn,
GetParam(),
ackFrame,
[&](const auto&, const auto&, const auto&) {},
testLossHandler(lostPackets),
Clock::now());
// Packet should be removed from outstandingPackets without triggering
// onPacketAckOrLoss
EXPECT_TRUE(conn.outstandingPackets.empty());
EXPECT_GT(conn.lossState.totalBytesAcked, 0);
}
TEST_P(AckHandlersTest, PureAckBytesSkipsCongestionControl) {
QuicServerConnectionState conn;
auto mockController = std::make_unique<MockCongestionController>();
auto rawController = mockController.get();
conn.congestionController = std::move(mockController);
auto regularPacket = createNewPacket(10, GetParam());
WriteAckFrame ack;
ack.ackBlocks.insert(2, 5);
conn.outstandingPackets.emplace_back(OutstandingPacket(
std::move(regularPacket), Clock::now(), 2, false, true, 2));
conn.outstandingPureAckPacketsCount++;
ReadAckFrame ackFrame;
ackFrame.largestAcked = 12;
ackFrame.ackBlocks.emplace_back(5, 12);
std::vector<PacketNum> lostPackets;
// onPacketAckOrLoss will be called, but the ackedBytes is 0. We only need the
// largestAckedPacket
EXPECT_CALL(*rawController, onPacketAckOrLoss(_, _))
.Times(1)
.WillOnce(Invoke([&](auto ackEvent, auto) {
EXPECT_EQ(0, ackEvent->ackedBytes);
EXPECT_EQ(10, ackEvent->largestAckedPacket.value());
EXPECT_FALSE(ackEvent->ackedPackets.empty());
EXPECT_EQ(1, ackEvent->ackedPackets.size());
EXPECT_EQ(2, ackEvent->ackedPackets.front().encodedSize);
EXPECT_FALSE(ackEvent->ackedPackets.front().isHandshake);
EXPECT_TRUE(ackEvent->ackedPackets.front().pureAck);
EXPECT_EQ(2, ackEvent->ackedPackets.front().totalBytesSent);
}));
processAckFrame(
conn,
GetParam(),
ackFrame,
[&](const auto&, const auto&, const auto&) {},
testLossHandler(lostPackets),
Clock::now());
// Packet should be removed from outstandingPackets without triggering
// onPacketAckOrLoss
EXPECT_TRUE(conn.outstandingPackets.empty());
}
TEST_P(AckHandlersTest, NoSkipAckVisitor) {
QuicServerConnectionState conn;
auto mockCongestionController = std::make_unique<MockCongestionController>();
auto rawCongestionController = mockCongestionController.get();
conn.congestionController = std::move(mockCongestionController);
EXPECT_CALL(*rawCongestionController, onPacketAckOrLoss(_, _))
.Times(1)
.WillOnce(Invoke([&](auto ackEvent, auto) {
EXPECT_EQ(1, ackEvent->ackedPackets.size());
EXPECT_EQ(1, ackEvent->ackedPackets.front().encodedSize);
EXPECT_FALSE(ackEvent->ackedPackets.front().pureAck);
EXPECT_FALSE(ackEvent->ackedPackets.front().isHandshake);
EXPECT_EQ(1, ackEvent->ackedPackets.front().totalBytesSent);
}));
PacketNum packetNum = 0;
auto regularPacket = createNewPacket(packetNum, GetParam());
// We need to at least have one frame to trigger ackVisitor
WriteStreamFrame frame(0, 0, 0, true);
regularPacket.frames.emplace_back(std::move(frame));
conn.outstandingPackets.emplace_back(OutstandingPacket(
std::move(regularPacket), Clock::now(), 1, false, false, 1));
ReadAckFrame ackFrame;
ackFrame.largestAcked = 0;
ackFrame.ackBlocks.emplace_back(0, 0);
uint16_t ackVisitorCounter = 0;
// A counting ack visitor
auto countingAckVisitor = [&](const auto& /* outstandingPacket */,
const auto& /* packetFrame */,
const auto& /* readAckFrame */) {
ackVisitorCounter++;
};
processAckFrame(
conn,
GetParam(),
ackFrame,
countingAckVisitor,
[&](auto& /*conn*/,
auto& /* packet */,
bool /* processed */,
PacketNum) { /* no-op lossVisitor */ },
Clock::now());
EXPECT_EQ(1, ackVisitorCounter);
}
TEST_P(AckHandlersTest, SkipAckVisitor) {
QuicServerConnectionState conn;
auto mockCongestionController = std::make_unique<MockCongestionController>();
auto rawCongestionController = mockCongestionController.get();
conn.congestionController = std::move(mockCongestionController);
EXPECT_CALL(*rawCongestionController, onPacketAckOrLoss(_, _))
.Times(1)
.WillOnce(Invoke([&](auto ackEvent, auto) {
EXPECT_EQ(1, ackEvent->ackedPackets.size());
EXPECT_EQ(1, ackEvent->ackedPackets.front().encodedSize);
EXPECT_FALSE(ackEvent->ackedPackets.front().pureAck);
EXPECT_FALSE(ackEvent->ackedPackets.front().isHandshake);
EXPECT_EQ(1, ackEvent->ackedPackets.front().totalBytesSent);
}));
PacketNum packetNum = 0;
auto regularPacket = createNewPacket(packetNum, GetParam());
// We need to at least have one frame to trigger ackVisitor
WriteStreamFrame frame(0, 0, 0, true);
regularPacket.frames.emplace_back(std::move(frame));
OutstandingPacket outstandingPacket(
std::move(regularPacket), Clock::now(), 1, false, false, 1);
// Give this outstandingPacket an associatedEvent that's not in
// outstandingPacketEvents
outstandingPacket.associatedEvent = 0;
conn.outstandingPackets.push_back(std::move(outstandingPacket));
conn.outstandingClonedPacketsCount++;
ReadAckFrame ackFrame;
ackFrame.largestAcked = 0;
ackFrame.ackBlocks.emplace_back(0, 0);
uint16_t ackVisitorCounter = 0;
// A counting ack visitor
auto countingAckVisitor = [&](const auto& /* outstandingPacket */,
const auto& /* packetFrame */,
const auto& /* readAckFrame */) {
ackVisitorCounter++;
};
processAckFrame(
conn,
GetParam(),
ackFrame,
countingAckVisitor,
[&](auto& /*conn*/,
auto& /* packet */,
bool /* processed */,
PacketNum) { /* no-op lossVisitor */ },
Clock::now());
EXPECT_EQ(0, ackVisitorCounter);
}
TEST_P(AckHandlersTest, NoDoubleProcess) {
QuicServerConnectionState conn;
conn.congestionController.reset();
WriteStreamFrame frame(0, 0, 0, true);
PacketNum packetNum1 = 0, packetNum2 = 1;
auto regularPacket1 = createNewPacket(packetNum1, GetParam()),
regularPacket2 = createNewPacket(packetNum2, GetParam());
regularPacket1.frames.push_back(frame);
regularPacket2.frames.push_back(frame);
OutstandingPacket outstandingPacket1(
std::move(regularPacket1), Clock::now(), 1, false, false, 1);
outstandingPacket1.associatedEvent = packetNum1;
OutstandingPacket outstandingPacket2(
std::move(regularPacket2), Clock::now(), 1, false, false, 1);
// The seconds packet has the same PacketEvent
outstandingPacket2.associatedEvent = packetNum1;
conn.outstandingPackets.push_back(std::move(outstandingPacket1));
conn.outstandingPackets.push_back(std::move(outstandingPacket2));
conn.outstandingClonedPacketsCount += 2;
conn.outstandingPacketEvents.insert(packetNum1);
// A counting ack visitor
uint16_t ackVisitorCounter = 0;
auto countingAckVisitor = [&](const auto& /* outstandingPacket */,
const auto& /* packetFrame */,
const auto& /* readAckFrame */) {
ackVisitorCounter++;
};
// First ack. This will ack first packet, and trigger a ack visiting.
ReadAckFrame ackFrame1;
ackFrame1.largestAcked = 0;
ackFrame1.ackBlocks.emplace_back(0, 0);
processAckFrame(
conn,
GetParam(),
ackFrame1,
countingAckVisitor,
[&](auto& /*conn*/,
auto& /* packet */,
bool /* processed */,
PacketNum) { /* no-op lossVisitor */ },
Clock::now());
EXPECT_EQ(1, ackVisitorCounter);
// Second ack that acks the second packet. This won't trigger a visit.
ReadAckFrame ackFrame2;
ackFrame2.largestAcked = 1;
ackFrame2.ackBlocks.emplace_back(1, 1);
processAckFrame(
conn,
GetParam(),
ackFrame2,
countingAckVisitor,
[&](auto& /* conn */,
auto& /* packet */,
bool /* processed */,
PacketNum) { /* no-op */ },
Clock::now());
EXPECT_EQ(1, ackVisitorCounter);
}
TEST_P(AckHandlersTest, ClonedPacketsCounter) {
QuicServerConnectionState conn;
conn.congestionController = nullptr;
WriteStreamFrame frame(0, 0, 0, true);
auto packetNum1 = conn.ackStates.appDataAckState.nextPacketNum;
auto regularPacket1 = createNewPacket(packetNum1, GetParam());
regularPacket1.frames.push_back(frame);
OutstandingPacket outstandingPacket1(
std::move(regularPacket1), Clock::now(), 1, false, false, 1);
outstandingPacket1.associatedEvent = packetNum1;
conn.ackStates.appDataAckState.nextPacketNum++;
auto packetNum2 = conn.ackStates.appDataAckState.nextPacketNum;
auto regularPacket2 = createNewPacket(packetNum2, GetParam());
regularPacket2.frames.push_back(frame);
OutstandingPacket outstandingPacket2(
std::move(regularPacket2), Clock::now(), 1, false, false, 1);
conn.outstandingPackets.push_back(std::move(outstandingPacket1));
conn.outstandingPackets.push_back(std::move(outstandingPacket2));
conn.outstandingClonedPacketsCount = 1;
conn.outstandingPacketEvents.insert(packetNum1);
ReadAckFrame ackFrame;
ackFrame.largestAcked = packetNum2;
ackFrame.ackBlocks.emplace_back(packetNum1, packetNum2);
uint16_t ackVisitorCounter = 0;
auto countingAckVisitor = [&](const auto& /* outstandingPacket */,
const auto& /* packetFrame */,
const auto& /* readAckFrame */) {
ackVisitorCounter++;
};
processAckFrame(
conn,
GetParam(),
ackFrame,
countingAckVisitor,
[&](auto& /* conn */,
auto& /* packet */,
bool /* processed */,
PacketNum) { /* no-op */ },
Clock::now());
EXPECT_EQ(2, ackVisitorCounter);
EXPECT_EQ(0, conn.outstandingClonedPacketsCount);
}
TEST_P(AckHandlersTest, UpdateMaxAckDelay) {
QuicServerConnectionState conn;
conn.congestionController = nullptr;
conn.lossState.mrtt = 200us;
PacketNum packetNum = 0;
auto regularPacket = createNewPacket(packetNum, GetParam());
auto sentTime = Clock::now();
conn.outstandingPackets.emplace_back(OutstandingPacket(
std::move(regularPacket), sentTime, 1, false, false, 1));
ReadAckFrame ackFrame;
// ackDelay has no effect on mrtt
ackFrame.ackDelay = 50us;
ackFrame.largestAcked = 0;
ackFrame.ackBlocks.emplace_back(0, 0);
auto receiveTime = sentTime + 10us;
processAckFrame(
conn,
GetParam(),
ackFrame,
[&](const auto&, const auto&, const auto&) { /* ackVisitor */ },
[&](auto&, auto&, bool, PacketNum) { /* lossVisitor */ },
receiveTime);
EXPECT_EQ(10us, conn.lossState.mrtt);
}
// Ack only acks packets aren't outstanding, but TimeReordering still finds loss
TEST_P(AckHandlersTest, AckNotOutstandingButLoss) {
QuicServerConnectionState conn;
auto qLogger = std::make_shared<FileQLogger>();
conn.qLogger = qLogger;
conn.lossState.srtt = 200ms;
conn.lossState.lrtt = 150ms;
// Packet 2 has been sent and acked:
conn.ackStates.appDataAckState.largestAckedByPeer = 2;
auto mockCongestionController = std::make_unique<MockCongestionController>();
auto rawCongestionController = mockCongestionController.get();
conn.congestionController = std::move(mockCongestionController);
EXPECT_CALL(*rawCongestionController, onPacketAckOrLoss(_, _))
.Times(1)
.WillOnce(Invoke(
[&](folly::Optional<CongestionController::AckEvent> ackEvent,
folly::Optional<CongestionController::LossEvent> lossEvent) {
EXPECT_FALSE(ackEvent->largestAckedPacket.hasValue());
EXPECT_TRUE(lossEvent->largestLostPacketNum.hasValue());
}));
// But packet 1 has been outstanding for longer than delayUntilLost:
PacketNum packetNum = 1;
auto regularPacket = createNewPacket(packetNum, PacketNumberSpace::AppData);
// We need to at least have one frame to trigger ackVisitor
WriteStreamFrame frame(0, 0, 0, true);
regularPacket.frames.emplace_back(std::move(frame));
auto delayUntilLost = 200ms * 9 / 8;
OutstandingPacket outstandingPacket(
std::move(regularPacket),
Clock::now() - delayUntilLost - 20ms,
1,
false,
false,
1);
conn.outstandingPackets.push_back(std::move(outstandingPacket));
conn.outstandingClonedPacketsCount++;
// Peer acks 2 again:
ReadAckFrame ackFrame;
ackFrame.largestAcked = 2;
ackFrame.ackBlocks.emplace_back(2, 2);
uint16_t ackVisitorCounter = 0;
conn.lossState.largestSent = 2;
// A counting ack visitor
auto countingAckVisitor = [&](const auto& /* outstandingPacket */,
const auto& /* packetFrame */,
const auto& /* readAckFrame */) {
ackVisitorCounter++;
};
processAckFrame(
conn,
PacketNumberSpace::AppData,
ackFrame,
countingAckVisitor,
[&](auto& /*conn*/,
auto& /* packet */,
bool /* processed */,
PacketNum) { /* no-op lossVisitor */ },
Clock::now());
EXPECT_EQ(0, ackVisitorCounter);
std::vector<int> indices =
getQLogEventIndices(QLogEventType::PacketsLost, qLogger);
EXPECT_EQ(indices.size(), 1);
auto tmp = std::move(qLogger->logs[indices[0]]);
auto event = dynamic_cast<QLogPacketsLostEvent*>(tmp.get());
EXPECT_EQ(event->largestLostPacketNum, 1);
EXPECT_EQ(event->lostBytes, 1);
EXPECT_EQ(event->lostPackets, 1);
}
TEST_P(AckHandlersTest, UpdatePendingAckStates) {
QuicServerConnectionState conn;
conn.congestionController = nullptr;
conn.lossState.totalBytesSent = 2468;
conn.lossState.totalBytesAcked = 1357;
PacketNum packetNum = 0;
auto regularPacket = createNewPacket(packetNum, GetParam());
auto sentTime = Clock::now() - 1500ms;
conn.outstandingPackets.emplace_back(OutstandingPacket(
std::move(regularPacket),
sentTime,
111,
false,
false,
conn.lossState.totalBytesSent + 111));
conn.lossState.totalBytesSent += 111;
ReadAckFrame ackFrame;
ackFrame.largestAcked = 0;
ackFrame.ackBlocks.emplace_back(0, 0);
auto receiveTime = Clock::now() - 200ms;
processAckFrame(
conn,
GetParam(),
ackFrame,
[&](auto, auto, auto) { /* ackVisitor */ },
[&](auto&, auto&, auto, auto) { /* lossVisitor */ },
receiveTime);
EXPECT_EQ(2468 + 111, conn.lossState.totalBytesSentAtLastAck);
EXPECT_EQ(1357 + 111, conn.lossState.totalBytesAckedAtLastAck);
EXPECT_EQ(sentTime, *conn.lossState.lastAckedPacketSentTime);
EXPECT_EQ(receiveTime, *conn.lossState.lastAckedTime);
EXPECT_EQ(111 + 1357, conn.lossState.totalBytesAcked);
}
TEST_F(AckHandlersTest, PureAckDoesNotUpdateRtt) {
QuicServerConnectionState conn;
conn.congestionController = nullptr;
PacketNum packetNum = 0;
auto regularPacket = createNewPacket(packetNum, PacketNumberSpace::AppData);
conn.outstandingPackets.emplace_back(OutstandingPacket(
std::move(regularPacket),
Clock::now() - 200ms,
111,
false /* handshake */,
true /* pureAck */,
111));
conn.outstandingPureAckPacketsCount++;
ASSERT_FALSE(conn.outstandingPackets.empty());
ReadAckFrame ackFrame;
ackFrame.largestAcked = packetNum;
ackFrame.ackDelay = 100us;
ackFrame.ackBlocks.emplace_back(packetNum, packetNum);
processAckFrame(
conn,
PacketNumberSpace::AppData,
ackFrame,
[&](const auto&, const auto&, const auto&) {},
[&](auto&, auto&, bool, auto) {},
Clock::now() - 150ms);
EXPECT_EQ(std::chrono::microseconds::max(), conn.lossState.mrtt);
EXPECT_EQ(0us, conn.lossState.srtt);
EXPECT_EQ(0us, conn.lossState.lrtt);
EXPECT_EQ(0us, conn.lossState.rttvar);
EXPECT_TRUE(conn.outstandingPackets.empty());
packetNum++;
regularPacket = createNewPacket(packetNum, PacketNumberSpace::AppData);
conn.outstandingPackets.emplace_back(OutstandingPacket(
std::move(regularPacket),
Clock::now() - 100ms,
111,
false /* handshake */,
false /* pureAck */,
111));
ASSERT_FALSE(conn.outstandingPackets.empty());
ackFrame.largestAcked = packetNum;
ackFrame.ackDelay = 100us;
ackFrame.ackBlocks.clear();
ackFrame.ackBlocks.emplace_back(packetNum, packetNum);
processAckFrame(
conn,
PacketNumberSpace::AppData,
ackFrame,
[&](const auto&, const auto&, const auto&) {},
[&](auto&, auto&, bool, auto) {},
Clock::now());
EXPECT_NE(std::chrono::microseconds::max(), conn.lossState.mrtt);
EXPECT_NE(0us, conn.lossState.srtt);
EXPECT_NE(0us, conn.lossState.lrtt);
EXPECT_NE(0us, conn.lossState.rttvar);
EXPECT_TRUE(conn.outstandingPackets.empty());
}
INSTANTIATE_TEST_CASE_P(
AckHandlersTests,
AckHandlersTest,
Values(
PacketNumberSpace::Initial,
PacketNumberSpace::Handshake,
PacketNumberSpace::AppData));
} // namespace test
} // namespace quic