#include "common.hpp"
#include "handles/BackoffTimerHandleInterface.hpp"
#include "RTC/SCTP/packet/Packet.hpp"
#include "RTC/SCTP/packet/UserData.hpp"
#include "RTC/SCTP/packet/chunks/AnyForwardTsnChunk.hpp"
#include "RTC/SCTP/packet/chunks/DataChunk.hpp"
#include "RTC/SCTP/packet/chunks/SackChunk.hpp"
#include "RTC/SCTP/public/SctpOptions.hpp"
#include "RTC/SCTP/tx/RetransmissionQueue.hpp"
#include "RTC/SCTP/tx/SendQueueInterface.hpp"
#include "Utils.hpp"
#include "test/include/RTC/SCTP/sctpCommon.hpp"
#include "test/include/catch2Macros.hpp"
#include "mocks/include/MockShared.hpp"
#include "mocks/include/RTC/SCTP/association/MockAssociationListener.hpp"
#include "mocks/include/RTC/SCTP/tx/MockSendQueue.hpp"
#include <catch2/catch_test_macros.hpp>
#include <vector>
SCENARIO("SCTP RetransmissionQueue", "[sctp][retransmissionqueue]")
{
sctpCommon::ResetBuffers();
class MockRetransmissionQueueListener : public RTC::SCTP::RetransmissionQueue::Listener
{
public:
void OnRetransmissionQueueNewRttMs(uint64_t rttMs) override
{
this->lastRttMs = rttMs;
}
void OnRetransmissionQueueClearRetransmissionCounter() override
{
++this->clearRetransmissionCounterCalls;
}
public:
uint64_t lastRttMs{ 0 };
size_t clearRetransmissionCounterCalls{ 0 };
};
class MockBackoffTimerHandleListener : public BackoffTimerHandleInterface::Listener
{
public:
void OnBackoffTimer(
BackoffTimerHandleInterface* , uint64_t& , bool& ) override
{
}
};
constexpr uint32_t Arwnd{ 100000 };
constexpr uint64_t Mtu{ 1191 };
constexpr uint32_t InitialTsn{ 10 };
const RTC::SCTP::SctpOptions sctpOptions{ .mtu = Mtu };
MockRetransmissionQueueListener retransmissionQueueListener;
MockBackoffTimerHandleListener backoffTimerHandleListener;
mocks::RTC::SCTP::MockAssociationListener associationListener;
mocks::RTC::SCTP::MockSendQueue sendQueue;
uint64_t nowMs{ 10000 };
mocks::MockShared shared(
[&nowMs]()
{
return nowMs;
});
const std::unique_ptr<BackoffTimerHandleInterface> t3RtxTimerUniquePtr{ shared.CreateBackoffTimer(
BackoffTimerHandleInterface::BackoffTimerHandleOptions{
.listener = std::addressof(backoffTimerHandleListener),
.label = "mock-sctp-t3-rtx",
.baseTimeoutMs = sctpOptions.initialRtoMs,
.backoffAlgorithm = BackoffTimerHandleInterface::BackoffAlgorithm::EXPONENTIAL,
.maxBackoffTimeoutMs = sctpOptions.timerMaxBackoffTimeoutMs,
.maxRestarts = std::nullopt }) };
auto* t3RtxTimer = t3RtxTimerUniquePtr.get();
auto createRetransmissionQueue =
[&retransmissionQueueListener, &associationListener, &sendQueue, &t3RtxTimer, &sctpOptions](
bool supportsPartialReliability = true, bool useMessageInterleaving = false)
{
return RTC::SCTP::RetransmissionQueue(
std::addressof(retransmissionQueueListener),
associationListener,
InitialTsn,
Arwnd,
sendQueue,
t3RtxTimer,
sctpOptions,
supportsPartialReliability,
useMessageInterleaving);
};
auto createDataToSend = [](
uint32_t outgoingMessageId,
uint16_t maxRetransmissions = RTC::SCTP::Types::MaxRetransmitsNoLimit)
{
return [outgoingMessageId, maxRetransmissions](uint64_t , size_t )
{
RTC::SCTP::UserData data(
1,
0,
0,
0,
53,
{ 0x01, 0x02, 0x03, 0x04 },
true,
true,
false);
RTC::SCTP::SendQueueInterface::DataToSend dataToSend(outgoingMessageId, std::move(data));
dataToSend.maxRetransmissions = maxRetransmissions;
return dataToSend;
};
};
auto createSackChunk = [sctpOptions](
uint32_t tsn,
uint32_t arwnd,
const std::vector<RTC::SCTP::SackChunk::GapAckBlock>&& gapAckBlocks = {})
{
std::unique_ptr<RTC::SCTP::SackChunk> chunk{ RTC::SCTP::SackChunk::Factory(
sctpCommon::FactoryBuffer, sctpOptions.mtu) };
chunk->SetCumulativeTsnAck(tsn);
chunk->SetAdvertisedReceiverWindowCredit(arwnd);
for (const auto& gapAckBlock : gapAckBlocks)
{
chunk->AddAckBlock(gapAckBlock);
}
return chunk;
};
auto getTSNsForFastRetransmit = [](RTC::SCTP::RetransmissionQueue& queue)
{
std::vector<uint32_t> tsns;
for (const auto& elem : queue.GetChunksForFastRetransmit(10000))
{
tsns.push_back(elem.first);
}
return tsns;
};
auto getSentPacketTSNs = [&nowMs](RTC::SCTP::RetransmissionQueue& queue, size_t maxLength = 10000)
{
std::vector<uint32_t> tsns;
for (const auto& elem : queue.GetChunksToSend(nowMs, maxLength))
{
tsns.push_back(elem.first);
}
return tsns;
};
SECTION("initial acked previous TSN")
{
auto retransmissionQueue = createRetransmissionQueue();
REQUIRE(retransmissionQueue.GetUnackedItems() == 0);
REQUIRE(retransmissionQueue.GetUnackedPacketBytes() == 0);
REQUIRE(retransmissionQueue.GetNextTsn() == InitialTsn);
REQUIRE(retransmissionQueue.ShouldSendForwardTsn(nowMs) == false);
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t, RTC::SCTP::OutstandingData::State>>{
{ InitialTsn - 1, RTC::SCTP::OutstandingData::State::ACKED },
});
}
SECTION("send one chunk")
{
auto retransmissionQueue = createRetransmissionQueue();
sendQueue.WillProduceOnce(createDataToSend(0))
.WillProduceRepeatedly(
[](uint64_t, size_t)
{
return std::nullopt;
});
REQUIRE(getSentPacketTSNs(retransmissionQueue) == std::vector<uint32_t>{ 10 });
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t, RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
{ 10, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
});
}
SECTION("send one chunk and ack")
{
auto retransmissionQueue = createRetransmissionQueue();
sendQueue.WillProduceOnce(createDataToSend(0))
.WillProduceRepeatedly(
[](uint64_t, size_t)
{
return std::nullopt;
});
REQUIRE(getSentPacketTSNs(retransmissionQueue) == std::vector<uint32_t>{ 10 });
retransmissionQueue.HandleReceivedSackChunk(nowMs, createSackChunk(10, Arwnd).get());
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 10, RTC::SCTP::OutstandingData::State::ACKED },
});
}
SECTION("send three chunks and ack two")
{
auto retransmissionQueue = createRetransmissionQueue();
sendQueue.WillProduceOnce(createDataToSend(0))
.WillProduceOnce(createDataToSend(1))
.WillProduceOnce(createDataToSend(2))
.WillProduceRepeatedly(
[](uint64_t, size_t)
{
return std::nullopt;
});
REQUIRE(getSentPacketTSNs(retransmissionQueue) == std::vector<uint32_t>{ 10, 11, 12 });
retransmissionQueue.HandleReceivedSackChunk(nowMs, createSackChunk(11, Arwnd).get());
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 11, RTC::SCTP::OutstandingData::State::ACKED },
{ 12, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
});
}
SECTION("ack with gap blocks from RFC 4960 section 334")
{
auto retransmissionQueue = createRetransmissionQueue();
sendQueue.WillProduceOnce(createDataToSend(0))
.WillProduceOnce(createDataToSend(1))
.WillProduceOnce(createDataToSend(2))
.WillProduceOnce(createDataToSend(3))
.WillProduceOnce(createDataToSend(4))
.WillProduceOnce(createDataToSend(5))
.WillProduceOnce(createDataToSend(6))
.WillProduceOnce(createDataToSend(7))
.WillProduceRepeatedly(
[](uint64_t, size_t)
{
return std::nullopt;
});
REQUIRE(
getSentPacketTSNs(retransmissionQueue) ==
std::vector<uint32_t>{ 10, 11, 12, 13, 14, 15, 16, 17 });
retransmissionQueue.HandleReceivedSackChunk(
nowMs,
createSackChunk(
12,
Arwnd,
{
{ 2, 3 },
{ 5, 5 }
})
.get());
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 12, RTC::SCTP::OutstandingData::State::ACKED },
{ 13, RTC::SCTP::OutstandingData::State::NACKED },
{ 14, RTC::SCTP::OutstandingData::State::ACKED },
{ 15, RTC::SCTP::OutstandingData::State::ACKED },
{ 16, RTC::SCTP::OutstandingData::State::NACKED },
{ 17, RTC::SCTP::OutstandingData::State::ACKED },
});
}
SECTION("resend packet when nacked three times")
{
auto retransmissionQueue = createRetransmissionQueue();
sendQueue.WillProduceOnce(createDataToSend(0))
.WillProduceOnce(createDataToSend(1))
.WillProduceOnce(createDataToSend(2))
.WillProduceOnce(createDataToSend(3))
.WillProduceOnce(createDataToSend(4))
.WillProduceOnce(createDataToSend(5))
.WillProduceOnce(createDataToSend(6))
.WillProduceOnce(createDataToSend(7))
.WillProduceRepeatedly(
[](uint64_t, size_t)
{
return std::nullopt;
});
REQUIRE(
getSentPacketTSNs(retransmissionQueue) ==
std::vector<uint32_t>{ 10, 11, 12, 13, 14, 15, 16, 17 });
sendQueue.WillProduceOnce(createDataToSend(8))
.WillProduceRepeatedly(
[](uint64_t, size_t)
{
return std::nullopt;
});
REQUIRE(getSentPacketTSNs(retransmissionQueue) == std::vector<uint32_t>{ 18 });
retransmissionQueue.HandleReceivedSackChunk(
nowMs,
createSackChunk(
12,
Arwnd,
{
{ 2, 3 },
{ 5, 6 }
})
.get());
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 12, RTC::SCTP::OutstandingData::State::ACKED },
{ 13, RTC::SCTP::OutstandingData::State::NACKED },
{ 14, RTC::SCTP::OutstandingData::State::ACKED },
{ 15, RTC::SCTP::OutstandingData::State::ACKED },
{ 16, RTC::SCTP::OutstandingData::State::NACKED },
{ 17, RTC::SCTP::OutstandingData::State::ACKED },
{ 18, RTC::SCTP::OutstandingData::State::ACKED },
});
sendQueue.WillProduceOnce(createDataToSend(9))
.WillProduceRepeatedly(
[](uint64_t, size_t)
{
return std::nullopt;
});
REQUIRE(getSentPacketTSNs(retransmissionQueue) == std::vector<uint32_t>{ 19 });
retransmissionQueue.HandleReceivedSackChunk(
nowMs,
createSackChunk(
12,
Arwnd,
{
{ 2, 3 },
{ 5, 7 }
})
.get());
sendQueue.WillProduceOnce(createDataToSend(10))
.WillProduceRepeatedly(
[](uint64_t, size_t)
{
return std::nullopt;
});
REQUIRE(getSentPacketTSNs(retransmissionQueue) == std::vector<uint32_t>{ 20 });
retransmissionQueue.HandleReceivedSackChunk(
nowMs,
createSackChunk(
12,
Arwnd,
{
{ 2, 3 },
{ 5, 8 }
})
.get());
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 12, RTC::SCTP::OutstandingData::State::ACKED },
{ 13, RTC::SCTP::OutstandingData::State::TO_BE_RETRANSMITTED },
{ 14, RTC::SCTP::OutstandingData::State::ACKED },
{ 15, RTC::SCTP::OutstandingData::State::ACKED },
{ 16, RTC::SCTP::OutstandingData::State::TO_BE_RETRANSMITTED },
{ 17, RTC::SCTP::OutstandingData::State::ACKED },
{ 18, RTC::SCTP::OutstandingData::State::ACKED },
{ 19, RTC::SCTP::OutstandingData::State::ACKED },
{ 20, RTC::SCTP::OutstandingData::State::ACKED },
});
sendQueue.ExpectProduceCalledTimes(0);
REQUIRE(getTSNsForFastRetransmit(retransmissionQueue) == std::vector<uint32_t>{ 13, 16 });
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 12, RTC::SCTP::OutstandingData::State::ACKED },
{ 13, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 14, RTC::SCTP::OutstandingData::State::ACKED },
{ 15, RTC::SCTP::OutstandingData::State::ACKED },
{ 16, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 17, RTC::SCTP::OutstandingData::State::ACKED },
{ 18, RTC::SCTP::OutstandingData::State::ACKED },
{ 19, RTC::SCTP::OutstandingData::State::ACKED },
{ 20, RTC::SCTP::OutstandingData::State::ACKED },
});
REQUIRE_VERIFICATION_RESULT(sendQueue.VerifyExpectations());
}
SECTION("restarts T3-rtx timer on retransmit first outstanding TSN")
{
auto retransmissionQueue = createRetransmissionQueue();
sendQueue.WillProduceOnce(createDataToSend(0))
.WillProduceOnce(createDataToSend(1))
.WillProduceOnce(createDataToSend(2))
.WillProduceRepeatedly(
[](uint64_t, size_t)
{
return std::nullopt;
});
nowMs = 100 * 1000;
REQUIRE(getSentPacketTSNs(retransmissionQueue) == std::vector<uint32_t>{ 10, 11, 12 });
nowMs += 100;
retransmissionQueue.HandleReceivedSackChunk(
nowMs,
createSackChunk(
10,
Arwnd,
{
{ 2, 2 },
})
.get());
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 10, RTC::SCTP::OutstandingData::State::ACKED },
{ 11, RTC::SCTP::OutstandingData::State::NACKED },
{ 12, RTC::SCTP::OutstandingData::State::ACKED },
});
sendQueue.WillProduceOnce(createDataToSend(3))
.WillProduceRepeatedly(
[](uint64_t, size_t)
{
return std::nullopt;
});
REQUIRE(getSentPacketTSNs(retransmissionQueue) == std::vector<uint32_t>{ 13 });
nowMs += 100;
retransmissionQueue.HandleReceivedSackChunk(
nowMs,
createSackChunk(
10,
Arwnd,
{
{ 2, 3 },
})
.get());
sendQueue.WillProduceOnce(createDataToSend(4))
.WillProduceRepeatedly(
[](uint64_t, size_t)
{
return std::nullopt;
});
REQUIRE(getSentPacketTSNs(retransmissionQueue) == std::vector<uint32_t>{ 14 });
nowMs += 100;
retransmissionQueue.HandleReceivedSackChunk(
nowMs,
createSackChunk(
10,
Arwnd,
{
{ 2, 4 },
})
.get());
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 10, RTC::SCTP::OutstandingData::State::ACKED },
{ 11, RTC::SCTP::OutstandingData::State::TO_BE_RETRANSMITTED },
{ 12, RTC::SCTP::OutstandingData::State::ACKED },
{ 13, RTC::SCTP::OutstandingData::State::ACKED },
{ 14, RTC::SCTP::OutstandingData::State::ACKED },
});
sendQueue.ExpectProduceCalledTimes(0);
REQUIRE(getTSNsForFastRetransmit(retransmissionQueue) == std::vector<uint32_t>{ 11 });
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 10, RTC::SCTP::OutstandingData::State::ACKED },
{ 11, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 12, RTC::SCTP::OutstandingData::State::ACKED },
{ 13, RTC::SCTP::OutstandingData::State::ACKED },
{ 14, RTC::SCTP::OutstandingData::State::ACKED },
});
nowMs += (sctpOptions.initialRtoMs - 1);
auto* backoffTimer = shared.GetBackoffTimer("mock-sctp-t3-rtx");
REQUIRE(backoffTimer);
REQUIRE(backoffTimer->EvaluateHasExpired() == false);
nowMs += 1;
REQUIRE(backoffTimer->EvaluateHasExpired() == true);
}
SECTION("can only produce two packets but wants to send three")
{
auto retransmissionQueue = createRetransmissionQueue();
sendQueue.WillProduceOnce(createDataToSend(0))
.WillProduceOnce(createDataToSend(1))
.WillProduceRepeatedly(
[](uint64_t, size_t)
{
return std::nullopt;
});
REQUIRE(getSentPacketTSNs(retransmissionQueue, 1000) == std::vector<uint32_t>{ 10, 11 });
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
{ 10, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 11, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
});
}
SECTION("retransmits on T3-rtx expiry")
{
auto retransmissionQueue = createRetransmissionQueue();
sendQueue.WillProduceOnce(createDataToSend(0))
.WillProduceRepeatedly(
[](uint64_t, size_t)
{
return std::nullopt;
});
REQUIRE(retransmissionQueue.ShouldSendForwardTsn(nowMs) == false);
REQUIRE(getSentPacketTSNs(retransmissionQueue, 1000) == std::vector<uint32_t>{ 10 });
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
{ 10, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
});
retransmissionQueue.HandleT3RtxTimerExpiry();
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
{ 10, RTC::SCTP::OutstandingData::State::TO_BE_RETRANSMITTED },
});
REQUIRE(retransmissionQueue.ShouldSendForwardTsn(nowMs) == false);
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
{ 10, RTC::SCTP::OutstandingData::State::TO_BE_RETRANSMITTED },
});
REQUIRE(getSentPacketTSNs(retransmissionQueue, 1000) == std::vector<uint32_t>{ 10 });
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
{ 10, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
});
}
SECTION("limited retransmission only with RFC 3758 support")
{
auto retransmissionQueue = createRetransmissionQueue( false);
sendQueue.WillProduceOnce(createDataToSend(42, 0))
.WillProduceRepeatedly(
[](uint64_t, size_t)
{
return std::nullopt;
});
REQUIRE(retransmissionQueue.ShouldSendForwardTsn(nowMs) == false);
REQUIRE(getSentPacketTSNs(retransmissionQueue, 1000) == std::vector<uint32_t>{ 10 });
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
{ 10, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
});
retransmissionQueue.HandleT3RtxTimerExpiry();
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
{ 10, RTC::SCTP::OutstandingData::State::TO_BE_RETRANSMITTED },
});
sendQueue.ExpectDiscardCalledTimes(0);
REQUIRE(retransmissionQueue.ShouldSendForwardTsn(nowMs) == false);
REQUIRE_VERIFICATION_RESULT(sendQueue.VerifyExpectations());
}
SECTION("limits retransmissions as UDP")
{
auto retransmissionQueue = createRetransmissionQueue();
sendQueue.WillProduceOnce(createDataToSend(42, 0))
.WillProduceRepeatedly(
[](uint64_t, size_t)
{
return std::nullopt;
});
REQUIRE(retransmissionQueue.ShouldSendForwardTsn(nowMs) == false);
REQUIRE(getSentPacketTSNs(retransmissionQueue) == std::vector<uint32_t>{ 10 });
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
{ 10, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
});
sendQueue.WillDiscardOnce(1, 42, false);
retransmissionQueue.HandleT3RtxTimerExpiry();
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
{ 10, RTC::SCTP::OutstandingData::State::ABANDONED },
});
REQUIRE(retransmissionQueue.ShouldSendForwardTsn(nowMs) == true);
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
{ 10, RTC::SCTP::OutstandingData::State::ABANDONED },
});
REQUIRE(getSentPacketTSNs(retransmissionQueue, 1000).empty());
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
{ 10, RTC::SCTP::OutstandingData::State::ABANDONED },
});
}
SECTION("limits retransmissions to three sends")
{
auto retransmissionQueue = createRetransmissionQueue();
sendQueue.WillProduceOnce(createDataToSend(42, 3))
.WillProduceRepeatedly(
[](uint64_t, size_t)
{
return std::nullopt;
});
REQUIRE(retransmissionQueue.ShouldSendForwardTsn(nowMs) == false);
REQUIRE(getSentPacketTSNs(retransmissionQueue, 1000) == std::vector<uint32_t>{ 10 });
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
{ 10, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
});
sendQueue.ExpectDiscardCalledTimes(0);
retransmissionQueue.HandleT3RtxTimerExpiry();
REQUIRE(retransmissionQueue.ShouldSendForwardTsn(nowMs) == false);
REQUIRE(retransmissionQueue.GetChunksToSend(nowMs, 1000).size() == 1);
retransmissionQueue.HandleT3RtxTimerExpiry();
REQUIRE(retransmissionQueue.ShouldSendForwardTsn(nowMs) == false);
REQUIRE(retransmissionQueue.GetChunksToSend(nowMs, 1000).size() == 1);
retransmissionQueue.HandleT3RtxTimerExpiry();
REQUIRE(retransmissionQueue.ShouldSendForwardTsn(nowMs) == false);
REQUIRE(retransmissionQueue.GetChunksToSend(nowMs, 1000).size() == 1);
REQUIRE_VERIFICATION_RESULT(sendQueue.VerifyExpectations());
sendQueue.WillDiscardOnce(1, 42, false);
retransmissionQueue.HandleT3RtxTimerExpiry();
REQUIRE(retransmissionQueue.ShouldSendForwardTsn(nowMs) == true);
REQUIRE(retransmissionQueue.GetChunksToSend(nowMs, 1000).empty());
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
{ 10, RTC::SCTP::OutstandingData::State::ABANDONED },
});
}
SECTION("retransmits when send buffer is full on T3-rtx expiry")
{
auto retransmissionQueue = createRetransmissionQueue();
constexpr size_t Cwnd{ 1200 };
retransmissionQueue.SetCwnd(Cwnd);
REQUIRE(retransmissionQueue.GetCwnd() == Cwnd);
REQUIRE(retransmissionQueue.GetUnackedPacketBytes() == 0);
REQUIRE(retransmissionQueue.GetUnackedItems() == 0);
const std::vector<uint8_t> payload(1000, 0x00);
sendQueue
.WillProduceOnce(
[&payload](uint64_t , size_t )
{
return RTC::SCTP::SendQueueInterface::DataToSend(
0, RTC::SCTP::UserData(1, 0, 0, 0, 53, payload, true, true, false));
})
.WillProduceRepeatedly(
[](uint64_t, size_t)
{
return std::nullopt;
});
REQUIRE(getSentPacketTSNs(retransmissionQueue, 1500) == std::vector<uint32_t>{ 10 });
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
{ 10, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
});
REQUIRE(
retransmissionQueue.GetUnackedPacketBytes() ==
payload.size() + RTC::SCTP::DataChunk::DataChunkHeaderLength);
REQUIRE(retransmissionQueue.GetUnackedItems() == 1);
retransmissionQueue.HandleT3RtxTimerExpiry();
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
{ 10, RTC::SCTP::OutstandingData::State::TO_BE_RETRANSMITTED },
});
REQUIRE(retransmissionQueue.GetUnackedPacketBytes() == 0);
REQUIRE(retransmissionQueue.GetUnackedItems() == 0);
REQUIRE(getSentPacketTSNs(retransmissionQueue, 1500) == std::vector<uint32_t>{ 10 });
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
{ 10, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
});
REQUIRE(
retransmissionQueue.GetUnackedPacketBytes() ==
payload.size() + RTC::SCTP::DataChunk::DataChunkHeaderLength);
REQUIRE(retransmissionQueue.GetUnackedItems() == 1);
}
SECTION("produces valid FORWARD-TSN")
{
auto retransmissionQueue = createRetransmissionQueue();
sendQueue
.WillProduceOnce(
[](uint64_t , size_t )
{
RTC::SCTP::UserData data(1, 42, 0, 0, 53, { 0x01, 0x02, 0x03, 0x04 }, true, false, false);
RTC::SCTP::SendQueueInterface::DataToSend dataToSend(42, std::move(data));
dataToSend.maxRetransmissions = 0;
return dataToSend;
})
.WillProduceOnce(
[](uint64_t , size_t )
{
RTC::SCTP::UserData data(1, 42, 0, 0, 53, { 0x05, 0x06, 0x07, 0x08 }, false, false, false);
RTC::SCTP::SendQueueInterface::DataToSend dataToSend(42, std::move(data));
dataToSend.maxRetransmissions = 0;
return dataToSend;
})
.WillProduceOnce(
[](uint64_t , size_t )
{
RTC::SCTP::UserData data(1, 42, 0, 0, 53, { 0x09, 0x0a, 0x0b, 0x0c }, false, false, false);
RTC::SCTP::SendQueueInterface::DataToSend dataToSend(42, std::move(data));
dataToSend.maxRetransmissions = 0;
return dataToSend;
})
.WillProduceRepeatedly(
[](uint64_t, size_t)
{
return std::nullopt;
});
REQUIRE(getSentPacketTSNs(retransmissionQueue, 1000) == std::vector<uint32_t>{ 10, 11, 12 });
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
{ 10, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 11, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 12, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
});
retransmissionQueue.HandleReceivedSackChunk(nowMs, createSackChunk(10, Arwnd).get());
sendQueue.WillDiscardOnce(1, 42, true);
retransmissionQueue.HandleT3RtxTimerExpiry();
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 10, RTC::SCTP::OutstandingData::State::ACKED },
{ 11, RTC::SCTP::OutstandingData::State::ABANDONED },
{ 12, RTC::SCTP::OutstandingData::State::ABANDONED },
{ 13, RTC::SCTP::OutstandingData::State::ABANDONED },
});
REQUIRE(retransmissionQueue.ShouldSendForwardTsn(nowMs) == true);
const std::unique_ptr<RTC::SCTP::Packet> packet{ RTC::SCTP::Packet::Factory(
sctpCommon::FactoryBuffer, sctpOptions.mtu) };
const auto* forwardTsnChunk = retransmissionQueue.AddForwardTsn(packet.get());
REQUIRE(forwardTsnChunk);
REQUIRE(forwardTsnChunk->GetNewCumulativeTsn() == 13);
REQUIRE(
forwardTsnChunk->GetSkippedStreams() ==
std::vector<RTC::SCTP::AnyForwardTsnChunk::SkippedStream>{
{ 1, 42 }
});
}
SECTION("produces valid FORWARD-TSN when fully sent")
{
auto retransmissionQueue = createRetransmissionQueue();
sendQueue
.WillProduceOnce(
[](uint64_t , size_t )
{
RTC::SCTP::UserData data(1, 42, 0, 0, 53, { 0x01, 0x02, 0x03, 0x04 }, true, false, false);
RTC::SCTP::SendQueueInterface::DataToSend dataToSend(42, std::move(data));
dataToSend.maxRetransmissions = 0;
return dataToSend;
})
.WillProduceOnce(
[](uint64_t , size_t )
{
RTC::SCTP::UserData data(1, 42, 0, 0, 53, { 0x05, 0x06, 0x07, 0x08 }, false, false, false);
RTC::SCTP::SendQueueInterface::DataToSend dataToSend(42, std::move(data));
dataToSend.maxRetransmissions = 0;
return dataToSend;
})
.WillProduceOnce(
[](uint64_t , size_t )
{
RTC::SCTP::UserData data(1, 42, 0, 0, 53, { 0x09, 0x0a, 0x0b, 0x0c }, false, true, false);
RTC::SCTP::SendQueueInterface::DataToSend dataToSend(42, std::move(data));
dataToSend.maxRetransmissions = 0;
return dataToSend;
})
.WillProduceRepeatedly(
[](uint64_t, size_t)
{
return std::nullopt;
});
REQUIRE(getSentPacketTSNs(retransmissionQueue, 1000) == std::vector<uint32_t>{ 10, 11, 12 });
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
{ 10, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 11, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 12, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
});
retransmissionQueue.HandleReceivedSackChunk(nowMs, createSackChunk(10, Arwnd).get());
sendQueue.WillDiscardOnce(1, 42, false);
retransmissionQueue.HandleT3RtxTimerExpiry();
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 10, RTC::SCTP::OutstandingData::State::ACKED },
{ 11, RTC::SCTP::OutstandingData::State::ABANDONED },
{ 12, RTC::SCTP::OutstandingData::State::ABANDONED },
});
REQUIRE(retransmissionQueue.ShouldSendForwardTsn(nowMs) == true);
const std::unique_ptr<RTC::SCTP::Packet> packet{ RTC::SCTP::Packet::Factory(
sctpCommon::FactoryBuffer, sctpOptions.mtu) };
const auto* forwardTsnChunk = retransmissionQueue.AddForwardTsn(packet.get());
REQUIRE(forwardTsnChunk);
REQUIRE(forwardTsnChunk->GetNewCumulativeTsn() == 12);
REQUIRE(
forwardTsnChunk->GetSkippedStreams() ==
std::vector<RTC::SCTP::AnyForwardTsnChunk::SkippedStream>{
{ 1, 42 }
});
}
SECTION("produces valid I-FORWARD-TSN")
{
auto retransmissionQueue = createRetransmissionQueue(
true, true);
sendQueue
.WillProduceOnce(
[](uint64_t , size_t )
{
RTC::SCTP::UserData data(1, 0, 42, 0, 53, { 0x01, 0x02, 0x03, 0x04 }, true, false, false);
RTC::SCTP::SendQueueInterface::DataToSend dataToSend(42, std::move(data));
dataToSend.maxRetransmissions = 0;
return dataToSend;
})
.WillProduceOnce(
[](uint64_t , size_t )
{
RTC::SCTP::UserData data(2, 0, 42, 0, 53, { 0x01, 0x02, 0x03, 0x04 }, true, false, true);
RTC::SCTP::SendQueueInterface::DataToSend dataToSend(43, std::move(data));
dataToSend.maxRetransmissions = 0;
return dataToSend;
})
.WillProduceOnce(
[](uint64_t , size_t )
{
RTC::SCTP::UserData data(3, 0, 42, 0, 53, { 0x09, 0x0a, 0x0b, 0x0c }, true, false, false);
RTC::SCTP::SendQueueInterface::DataToSend dataToSend(44, std::move(data));
dataToSend.maxRetransmissions = 0;
return dataToSend;
})
.WillProduceOnce(
[](uint64_t , size_t )
{
RTC::SCTP::UserData data(4, 0, 42, 0, 53, { 0x0d, 0x0e, 0x0f, 0x10 }, true, false, false);
RTC::SCTP::SendQueueInterface::DataToSend dataToSend(45, std::move(data));
dataToSend.maxRetransmissions = 0;
return dataToSend;
})
.WillProduceRepeatedly(
[](uint64_t, size_t)
{
return std::nullopt;
});
REQUIRE(getSentPacketTSNs(retransmissionQueue, 1000) == std::vector<uint32_t>{ 10, 11, 12, 13 });
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
{ 10, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 11, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 12, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 13, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
});
retransmissionQueue.HandleReceivedSackChunk(
nowMs,
createSackChunk(
9,
Arwnd,
{
{ 4, 4 },
})
.get());
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
{ 10, RTC::SCTP::OutstandingData::State::NACKED },
{ 11, RTC::SCTP::OutstandingData::State::NACKED },
{ 12, RTC::SCTP::OutstandingData::State::NACKED },
{ 13, RTC::SCTP::OutstandingData::State::ACKED },
});
sendQueue.WillDiscardOnce(1, 42, true);
sendQueue.WillDiscardOnce(2, 43, true);
sendQueue.WillDiscardOnce(3, 44, true);
retransmissionQueue.HandleT3RtxTimerExpiry();
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
{ 10, RTC::SCTP::OutstandingData::State::ABANDONED },
{ 11, RTC::SCTP::OutstandingData::State::ABANDONED },
{ 12, RTC::SCTP::OutstandingData::State::ABANDONED },
{ 13, RTC::SCTP::OutstandingData::State::ACKED },
{ 14, RTC::SCTP::OutstandingData::State::ABANDONED },
{ 15, RTC::SCTP::OutstandingData::State::ABANDONED },
{ 16, RTC::SCTP::OutstandingData::State::ABANDONED },
});
REQUIRE(retransmissionQueue.ShouldSendForwardTsn(nowMs) == true);
std::unique_ptr<RTC::SCTP::Packet> packet{ RTC::SCTP::Packet::Factory(
sctpCommon::FactoryBuffer, sctpOptions.mtu) };
const auto* iForwardTsnChunk1 = retransmissionQueue.AddIForwardTsn(packet.get());
REQUIRE(iForwardTsnChunk1);
REQUIRE(iForwardTsnChunk1->GetNewCumulativeTsn() == 12);
REQUIRE(
iForwardTsnChunk1->GetSkippedStreams() ==
std::vector<RTC::SCTP::AnyForwardTsnChunk::SkippedStream>{
{ false, 1, 42 },
{ false, 3, 42 },
{ true, 2, 42 },
});
retransmissionQueue.HandleReceivedSackChunk(nowMs, createSackChunk(12, Arwnd).get());
sendQueue.ExpectDiscardCalledTimes(0);
REQUIRE(retransmissionQueue.ShouldSendForwardTsn(nowMs) == false);
REQUIRE_VERIFICATION_RESULT(sendQueue.VerifyExpectations());
retransmissionQueue.HandleReceivedSackChunk(nowMs, createSackChunk(13, Arwnd).get());
REQUIRE(retransmissionQueue.ShouldSendForwardTsn(nowMs) == true);
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 13, RTC::SCTP::OutstandingData::State::ACKED },
{ 14, RTC::SCTP::OutstandingData::State::ABANDONED },
{ 15, RTC::SCTP::OutstandingData::State::ABANDONED },
{ 16, RTC::SCTP::OutstandingData::State::ABANDONED },
});
packet.reset(RTC::SCTP::Packet::Factory(sctpCommon::FactoryBuffer, sctpOptions.mtu));
const auto* iForwardTsnChunk2 = retransmissionQueue.AddIForwardTsn(packet.get());
REQUIRE(iForwardTsnChunk2);
REQUIRE(iForwardTsnChunk2->GetNewCumulativeTsn() == 16);
REQUIRE(
iForwardTsnChunk2->GetSkippedStreams() ==
std::vector<RTC::SCTP::AnyForwardTsnChunk::SkippedStream>{
{ false, 1, 42 },
{ false, 3, 42 },
{ true, 2, 42 },
});
}
SECTION("measure RTT")
{
auto retransmissionQueue = createRetransmissionQueue(
true, true);
sendQueue.WillProduceOnce(createDataToSend(0, 0))
.WillProduceRepeatedly(
[](uint64_t, size_t)
{
return std::nullopt;
});
REQUIRE(getSentPacketTSNs(retransmissionQueue) == std::vector<uint32_t>{ 10 });
constexpr uint64_t DurationMs{ 123 };
nowMs += DurationMs;
retransmissionQueue.HandleReceivedSackChunk(nowMs, createSackChunk(10, Arwnd).get());
REQUIRE(retransmissionQueueListener.lastRttMs == DurationMs);
}
SECTION("validate cumulative TSN at rest")
{
auto retransmissionQueue = createRetransmissionQueue();
REQUIRE(
retransmissionQueue.HandleReceivedSackChunk(nowMs, createSackChunk(8, Arwnd).get()) == false);
REQUIRE(
retransmissionQueue.HandleReceivedSackChunk(nowMs, createSackChunk(9, Arwnd).get()) == true);
REQUIRE(
retransmissionQueue.HandleReceivedSackChunk(nowMs, createSackChunk(10, Arwnd).get()) == false);
}
SECTION("validate cumulative TSN ack on inflight data")
{
auto retransmissionQueue = createRetransmissionQueue();
sendQueue.WillProduceOnce(createDataToSend(0))
.WillProduceOnce(createDataToSend(1))
.WillProduceOnce(createDataToSend(2))
.WillProduceOnce(createDataToSend(3))
.WillProduceOnce(createDataToSend(4))
.WillProduceOnce(createDataToSend(5))
.WillProduceOnce(createDataToSend(6))
.WillProduceOnce(createDataToSend(7))
.WillProduceRepeatedly(
[](uint64_t, size_t)
{
return std::nullopt;
});
REQUIRE(
getSentPacketTSNs(retransmissionQueue) ==
std::vector<uint32_t>{ 10, 11, 12, 13, 14, 15, 16, 17 });
REQUIRE(
retransmissionQueue.HandleReceivedSackChunk(nowMs, createSackChunk(8, Arwnd).get()) == false);
REQUIRE(
retransmissionQueue.HandleReceivedSackChunk(nowMs, createSackChunk(9, Arwnd).get()) == true);
REQUIRE(
retransmissionQueue.HandleReceivedSackChunk(nowMs, createSackChunk(10, Arwnd).get()) == true);
REQUIRE(
retransmissionQueue.HandleReceivedSackChunk(nowMs, createSackChunk(11, Arwnd).get()) == true);
REQUIRE(
retransmissionQueue.HandleReceivedSackChunk(nowMs, createSackChunk(12, Arwnd).get()) == true);
REQUIRE(
retransmissionQueue.HandleReceivedSackChunk(nowMs, createSackChunk(13, Arwnd).get()) == true);
REQUIRE(
retransmissionQueue.HandleReceivedSackChunk(nowMs, createSackChunk(14, Arwnd).get()) == true);
REQUIRE(
retransmissionQueue.HandleReceivedSackChunk(nowMs, createSackChunk(15, Arwnd).get()) == true);
REQUIRE(
retransmissionQueue.HandleReceivedSackChunk(nowMs, createSackChunk(16, Arwnd).get()) == true);
REQUIRE(
retransmissionQueue.HandleReceivedSackChunk(nowMs, createSackChunk(17, Arwnd).get()) == true);
REQUIRE(
retransmissionQueue.HandleReceivedSackChunk(nowMs, createSackChunk(18, Arwnd).get()) == false);
}
SECTION("handle gap-ack-blocks matching no inflight data")
{
auto retransmissionQueue = createRetransmissionQueue();
sendQueue.WillProduceOnce(createDataToSend(0))
.WillProduceOnce(createDataToSend(1))
.WillProduceOnce(createDataToSend(2))
.WillProduceOnce(createDataToSend(3))
.WillProduceOnce(createDataToSend(4))
.WillProduceOnce(createDataToSend(5))
.WillProduceOnce(createDataToSend(6))
.WillProduceOnce(createDataToSend(7))
.WillProduceRepeatedly(
[](uint64_t, size_t)
{
return std::nullopt;
});
REQUIRE(
getSentPacketTSNs(retransmissionQueue) ==
std::vector<uint32_t>{ 10, 11, 12, 13, 14, 15, 16, 17 });
retransmissionQueue.HandleReceivedSackChunk(
nowMs,
createSackChunk(
9,
Arwnd,
{
{ 11, 16 },
})
.get());
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
{ 10, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 11, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 12, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 13, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 14, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 15, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 16, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 17, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
});
}
SECTION("handle invalid gap-ack-blocks")
{
auto retransmissionQueue = createRetransmissionQueue();
retransmissionQueue.HandleReceivedSackChunk(
nowMs,
createSackChunk(
9,
Arwnd,
{
{ 3, 4 },
})
.get());
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
});
}
SECTION("gap-ack-blocks do not move cumulative TSN ack")
{
auto retransmissionQueue = createRetransmissionQueue();
sendQueue.WillProduceOnce(createDataToSend(0))
.WillProduceOnce(createDataToSend(1))
.WillProduceOnce(createDataToSend(2))
.WillProduceOnce(createDataToSend(3))
.WillProduceOnce(createDataToSend(4))
.WillProduceOnce(createDataToSend(5))
.WillProduceOnce(createDataToSend(6))
.WillProduceOnce(createDataToSend(7))
.WillProduceRepeatedly(
[](uint64_t, size_t)
{
return std::nullopt;
});
REQUIRE(
getSentPacketTSNs(retransmissionQueue) ==
std::vector<uint32_t>{ 10, 11, 12, 13, 14, 15, 16, 17 });
retransmissionQueue.HandleReceivedSackChunk(
nowMs,
createSackChunk(
9,
Arwnd,
{
{ 1, 5 },
})
.get());
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
{ 10, RTC::SCTP::OutstandingData::State::ACKED },
{ 11, RTC::SCTP::OutstandingData::State::ACKED },
{ 12, RTC::SCTP::OutstandingData::State::ACKED },
{ 13, RTC::SCTP::OutstandingData::State::ACKED },
{ 14, RTC::SCTP::OutstandingData::State::ACKED },
{ 15, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 16, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 17, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
});
}
SECTION("stays within available size")
{
auto retransmissionQueue = createRetransmissionQueue();
constexpr size_t AvailableBytes{ 1188 - 12 };
bool sizeCheck1Ok{ false };
bool sizeCheck2Ok{ false };
sendQueue
.WillProduceOnce(
[&sizeCheck1Ok](uint64_t , size_t maxLength)
{
sizeCheck1Ok = (maxLength == AvailableBytes - RTC::SCTP::DataChunk::DataChunkHeaderLength);
std::vector<uint8_t> payload(183, 0x00);
return RTC::SCTP::SendQueueInterface::DataToSend(
0, RTC::SCTP::UserData(1, 0, 0, 0, 53, std::move(payload), true, true, false));
})
.WillProduceOnce(
[&sizeCheck2Ok](uint64_t , size_t maxLength)
{
sizeCheck2Ok = (maxLength == 976 - RTC::SCTP::DataChunk::DataChunkHeaderLength);
std::vector<uint8_t> payload(957, 0x00);
return RTC::SCTP::SendQueueInterface::DataToSend(
1, RTC::SCTP::UserData(1, 0, 0, 0, 53, std::move(payload), true, true, false));
});
REQUIRE(getSentPacketTSNs(retransmissionQueue, AvailableBytes) == std::vector<uint32_t>{ 10, 11 });
REQUIRE(sizeCheck1Ok == true);
REQUIRE(sizeCheck2Ok == true);
}
SECTION("accounts nacked abandoned chunks as not outstanding")
{
auto retransmissionQueue = createRetransmissionQueue();
const size_t chunkSerializedLength = RTC::SCTP::DataChunk::DataChunkHeaderLength + 4;
REQUIRE(chunkSerializedLength == 16 + 4);
sendQueue
.WillProduceOnce(
[](uint64_t , size_t )
{
RTC::SCTP::UserData data(1, 0, 0, 0, 53, { 0x01, 0x02, 0x03, 0x04 }, true, false, false);
RTC::SCTP::SendQueueInterface::DataToSend dataToSend(42, std::move(data));
dataToSend.maxRetransmissions = 0;
return dataToSend;
})
.WillProduceOnce(
[](uint64_t , size_t )
{
RTC::SCTP::UserData data(1, 0, 0, 0, 53, { 0x05, 0x06, 0x07, 0x08 }, false, false, false);
RTC::SCTP::SendQueueInterface::DataToSend dataToSend(42, std::move(data));
dataToSend.maxRetransmissions = 0;
return dataToSend;
})
.WillProduceOnce(
[](uint64_t , size_t )
{
RTC::SCTP::UserData data(1, 0, 0, 0, 53, { 0x09, 0x0a, 0x0b, 0x0c }, false, false, false);
RTC::SCTP::SendQueueInterface::DataToSend dataToSend(42, std::move(data));
dataToSend.maxRetransmissions = 0;
return dataToSend;
})
.WillProduceRepeatedly(
[](uint64_t, size_t)
{
return std::nullopt;
});
REQUIRE(getSentPacketTSNs(retransmissionQueue, 1000) == std::vector<uint32_t>{ 10, 11, 12 });
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
{ 10, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 11, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 12, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
});
REQUIRE(retransmissionQueue.GetUnackedPacketBytes() == chunkSerializedLength * 3);
REQUIRE(retransmissionQueue.GetUnackedItems() == 3);
sendQueue.WillDiscardOnce(1, 42, false);
retransmissionQueue.HandleT3RtxTimerExpiry();
REQUIRE(retransmissionQueue.ShouldSendForwardTsn(nowMs) == true);
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
{ 10, RTC::SCTP::OutstandingData::State::ABANDONED },
{ 11, RTC::SCTP::OutstandingData::State::ABANDONED },
{ 12, RTC::SCTP::OutstandingData::State::ABANDONED },
});
REQUIRE(retransmissionQueue.GetUnackedPacketBytes() == 0);
REQUIRE(retransmissionQueue.GetUnackedItems() == 0);
retransmissionQueue.HandleReceivedSackChunk(nowMs, createSackChunk(10, Arwnd).get());
REQUIRE(retransmissionQueue.GetUnackedPacketBytes() == 0);
REQUIRE(retransmissionQueue.GetUnackedItems() == 0);
retransmissionQueue.HandleReceivedSackChunk(nowMs, createSackChunk(11, Arwnd).get());
REQUIRE(retransmissionQueue.GetUnackedPacketBytes() == 0);
REQUIRE(retransmissionQueue.GetUnackedItems() == 0);
retransmissionQueue.HandleReceivedSackChunk(nowMs, createSackChunk(12, Arwnd).get());
REQUIRE(retransmissionQueue.GetUnackedPacketBytes() == 0);
REQUIRE(retransmissionQueue.GetUnackedItems() == 0);
}
SECTION("expire from send queue when partially sent")
{
auto retransmissionQueue = createRetransmissionQueue();
const uint64_t expiresAtMs = nowMs + 10;
sendQueue
.WillProduceOnce(
[expiresAtMs](uint64_t , size_t )
{
RTC::SCTP::UserData data(17, 0, 0, 0, 53, { 0x01, 0x02, 0x03, 0x04 }, true, false, false);
RTC::SCTP::SendQueueInterface::DataToSend dataToSend(42, std::move(data));
dataToSend.expiresAtMs = expiresAtMs;
return dataToSend;
})
.WillProduceOnce(
[expiresAtMs](uint64_t , size_t )
{
RTC::SCTP::UserData data(17, 0, 0, 0, 53, { 0x05, 0x06, 0x07, 0x08 }, false, false, false);
RTC::SCTP::SendQueueInterface::DataToSend dataToSend(42, std::move(data));
dataToSend.expiresAtMs = expiresAtMs;
return dataToSend;
})
.WillProduceRepeatedly(
[](uint64_t, size_t)
{
return std::nullopt;
});
REQUIRE(getSentPacketTSNs(retransmissionQueue, 24) == std::vector<uint32_t>{ 10 });
nowMs += 100;
sendQueue.WillDiscardOnce(17, 42, true);
REQUIRE(retransmissionQueue.GetChunksToSend(nowMs, 24).empty());
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED }, { 10, RTC::SCTP::OutstandingData::State::ABANDONED }, { 11, RTC::SCTP::OutstandingData::State::ABANDONED }, { 12, RTC::SCTP::OutstandingData::State::ABANDONED }, });
}
SECTION("expire correct message from send queue")
{
auto retransmissionQueue = createRetransmissionQueue();
const uint64_t expiresAtMs = nowMs + 10;
sendQueue
.WillProduceOnce(
[expiresAtMs](uint64_t , size_t )
{
RTC::SCTP::UserData data(1, 0, 0, 0, 53, { 0x01, 0x02, 0x03, 0x04 }, true, true, false);
RTC::SCTP::SendQueueInterface::DataToSend dataToSend(42, std::move(data));
dataToSend.expiresAtMs = expiresAtMs;
return dataToSend;
})
.WillProduceOnce(
[expiresAtMs](uint64_t , size_t )
{
RTC::SCTP::UserData data(1, 0, 1, 0, 53, { 0x01, 0x02, 0x03, 0x04 }, true, true, false);
RTC::SCTP::SendQueueInterface::DataToSend dataToSend(43, std::move(data));
dataToSend.expiresAtMs = expiresAtMs;
return dataToSend;
})
.WillProduceOnce(
[expiresAtMs](uint64_t , size_t )
{
RTC::SCTP::UserData data(1, 0, 0, 0, 53, { 0x01, 0x02, 0x03, 0x04 }, true, false, false);
RTC::SCTP::SendQueueInterface::DataToSend dataToSend(44, std::move(data));
dataToSend.expiresAtMs = expiresAtMs;
return dataToSend;
})
.WillProduceOnce(
[expiresAtMs](uint64_t , size_t )
{
RTC::SCTP::UserData data(1, 0, 0, 0, 53, { 0x05, 0x06, 0x07, 0x08 }, false, false, false);
RTC::SCTP::SendQueueInterface::DataToSend dataToSend(44, std::move(data));
dataToSend.expiresAtMs = expiresAtMs;
return dataToSend;
})
.WillProduceRepeatedly(
[](uint64_t, size_t)
{
return std::nullopt;
});
std::vector<std::pair<uint32_t, RTC::SCTP::UserData>> expectedChunksToSend;
expectedChunksToSend.emplace_back(
10,
RTC::SCTP::UserData{
1, 0, 0, 0, 53, { 0x01, 0x02, 0x03, 0x04 },
true, true, false
});
REQUIRE(retransmissionQueue.GetChunksToSend(nowMs, 24) == expectedChunksToSend);
expectedChunksToSend.clear();
expectedChunksToSend.emplace_back(
11,
RTC::SCTP::UserData{
1, 0, 1, 0, 53, { 0x01, 0x02, 0x03, 0x04 },
true, true, false
});
REQUIRE(retransmissionQueue.GetChunksToSend(nowMs, 24) == expectedChunksToSend);
expectedChunksToSend.clear();
expectedChunksToSend.emplace_back(
12,
RTC::SCTP::UserData{
1, 0, 0, 0, 53, { 0x01, 0x02, 0x03, 0x04 },
true, false, false
});
REQUIRE(retransmissionQueue.GetChunksToSend(nowMs, 24) == expectedChunksToSend);
nowMs += 100;
sendQueue.WillDiscardOnce(1, 44, true);
REQUIRE(retransmissionQueue.GetChunksToSend(nowMs, 24).empty());
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED }, { 10, RTC::SCTP::OutstandingData::State::IN_FLIGHT }, { 11, RTC::SCTP::OutstandingData::State::IN_FLIGHT }, { 12, RTC::SCTP::OutstandingData::State::ABANDONED }, { 13, RTC::SCTP::OutstandingData::State::ABANDONED }, { 14, RTC::SCTP::OutstandingData::State::ABANDONED }, });
}
SECTION("limits retransmissions only when nacked three times")
{
auto retransmissionQueue = createRetransmissionQueue();
sendQueue.WillProduceOnce(createDataToSend(42, 0))
.WillProduceOnce(createDataToSend(0)) .WillProduceOnce(createDataToSend(1)) .WillProduceOnce(createDataToSend(2)) .WillProduceRepeatedly(
[](uint64_t, size_t)
{
return std::nullopt;
});
REQUIRE(retransmissionQueue.ShouldSendForwardTsn(nowMs) == false);
REQUIRE(getSentPacketTSNs(retransmissionQueue) == std::vector<uint32_t>{ 10, 11, 12, 13 });
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
{ 10, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 11, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 12, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 13, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
});
REQUIRE(retransmissionQueue.ShouldSendForwardTsn(nowMs) == false);
sendQueue.ExpectDiscardCalledTimes(0);
retransmissionQueue.HandleReceivedSackChunk(
nowMs,
createSackChunk(
9,
Arwnd,
{
{ 2, 2 },
})
.get());
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
{ 10, RTC::SCTP::OutstandingData::State::NACKED },
{ 11, RTC::SCTP::OutstandingData::State::ACKED },
{ 12, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 13, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
});
REQUIRE(retransmissionQueue.ShouldSendForwardTsn(nowMs) == false);
retransmissionQueue.HandleReceivedSackChunk(
nowMs,
createSackChunk(
9,
Arwnd,
{
{ 2, 3 },
})
.get());
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
{ 10, RTC::SCTP::OutstandingData::State::NACKED },
{ 11, RTC::SCTP::OutstandingData::State::ACKED },
{ 12, RTC::SCTP::OutstandingData::State::ACKED },
{ 13, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
});
REQUIRE(retransmissionQueue.ShouldSendForwardTsn(nowMs) == false);
REQUIRE_VERIFICATION_RESULT(sendQueue.VerifyExpectations());
sendQueue.WillDiscardOnce(1, 42, false);
retransmissionQueue.HandleReceivedSackChunk(
nowMs,
createSackChunk(
9,
Arwnd,
{
{ 2, 4 },
})
.get());
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
{ 10, RTC::SCTP::OutstandingData::State::ABANDONED },
{ 11, RTC::SCTP::OutstandingData::State::ACKED },
{ 12, RTC::SCTP::OutstandingData::State::ACKED },
{ 13, RTC::SCTP::OutstandingData::State::ACKED },
});
REQUIRE(retransmissionQueue.ShouldSendForwardTsn(nowMs) == true);
}
SECTION("abandons rtx limit 2 when nacked nine times")
{
auto retransmissionQueue = createRetransmissionQueue();
sendQueue.WillProduceOnce(createDataToSend(42, 2))
.WillProduceOnce(createDataToSend(0)) .WillProduceOnce(createDataToSend(1)) .WillProduceOnce(createDataToSend(2)) .WillProduceOnce(createDataToSend(3)) .WillProduceOnce(createDataToSend(4)) .WillProduceOnce(createDataToSend(5)) .WillProduceOnce(createDataToSend(6)) .WillProduceOnce(createDataToSend(7)) .WillProduceOnce(createDataToSend(8)) .WillProduceRepeatedly(
[](uint64_t, size_t)
{
return std::nullopt;
});
REQUIRE(retransmissionQueue.ShouldSendForwardTsn(nowMs) == false);
REQUIRE(
getSentPacketTSNs(retransmissionQueue) ==
std::vector<uint32_t>{ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 });
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
{ 10, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 11, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 12, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 13, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 14, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 15, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 16, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 17, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 18, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 19, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
});
sendQueue.ExpectDiscardCalledTimes(0);
for (uint32_t tsn{ 11 }; tsn <= 13; ++tsn)
{
retransmissionQueue.HandleReceivedSackChunk(
nowMs,
createSackChunk(
9,
Arwnd,
{
{ 2, static_cast<uint16_t>(tsn - 9) }
})
.get());
}
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
{ 10, RTC::SCTP::OutstandingData::State::TO_BE_RETRANSMITTED },
{ 11, RTC::SCTP::OutstandingData::State::ACKED },
{ 12, RTC::SCTP::OutstandingData::State::ACKED },
{ 13, RTC::SCTP::OutstandingData::State::ACKED },
{ 14, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 15, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 16, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 17, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 18, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 19, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
});
REQUIRE(getTSNsForFastRetransmit(retransmissionQueue) == std::vector<uint32_t>{ 10 });
for (uint32_t tsn{ 14 }; tsn <= 16; ++tsn)
{
retransmissionQueue.HandleReceivedSackChunk(
nowMs,
createSackChunk(
9,
Arwnd,
{
{ 2, static_cast<uint16_t>(tsn - 9) }
})
.get());
}
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
{ 10, RTC::SCTP::OutstandingData::State::TO_BE_RETRANSMITTED },
{ 11, RTC::SCTP::OutstandingData::State::ACKED },
{ 12, RTC::SCTP::OutstandingData::State::ACKED },
{ 13, RTC::SCTP::OutstandingData::State::ACKED },
{ 14, RTC::SCTP::OutstandingData::State::ACKED },
{ 15, RTC::SCTP::OutstandingData::State::ACKED },
{ 16, RTC::SCTP::OutstandingData::State::ACKED },
{ 17, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 18, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 19, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
});
REQUIRE(getSentPacketTSNs(retransmissionQueue, 1000) == std::vector<uint32_t>{ 10 });
for (uint32_t tsn{ 17 }; tsn <= 18; ++tsn)
{
retransmissionQueue.HandleReceivedSackChunk(
nowMs,
createSackChunk(
9,
Arwnd,
{
{ 2, static_cast<uint16_t>(tsn - 9) }
})
.get());
}
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
{ 10, RTC::SCTP::OutstandingData::State::NACKED },
{ 11, RTC::SCTP::OutstandingData::State::ACKED },
{ 12, RTC::SCTP::OutstandingData::State::ACKED },
{ 13, RTC::SCTP::OutstandingData::State::ACKED },
{ 14, RTC::SCTP::OutstandingData::State::ACKED },
{ 15, RTC::SCTP::OutstandingData::State::ACKED },
{ 16, RTC::SCTP::OutstandingData::State::ACKED },
{ 17, RTC::SCTP::OutstandingData::State::ACKED },
{ 18, RTC::SCTP::OutstandingData::State::ACKED },
{ 19, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
});
REQUIRE(retransmissionQueue.ShouldSendForwardTsn(nowMs) == false);
REQUIRE_VERIFICATION_RESULT(sendQueue.VerifyExpectations());
sendQueue.WillDiscardOnce(1, 42, false);
retransmissionQueue.HandleReceivedSackChunk(
nowMs,
createSackChunk(
9,
Arwnd,
{
{ 2, 10 }
})
.get());
REQUIRE(retransmissionQueue.GetChunksToSend(nowMs, 1000).empty());
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
{ 10, RTC::SCTP::OutstandingData::State::ABANDONED },
{ 11, RTC::SCTP::OutstandingData::State::ACKED },
{ 12, RTC::SCTP::OutstandingData::State::ACKED },
{ 13, RTC::SCTP::OutstandingData::State::ACKED },
{ 14, RTC::SCTP::OutstandingData::State::ACKED },
{ 15, RTC::SCTP::OutstandingData::State::ACKED },
{ 16, RTC::SCTP::OutstandingData::State::ACKED },
{ 17, RTC::SCTP::OutstandingData::State::ACKED },
{ 18, RTC::SCTP::OutstandingData::State::ACKED },
{ 19, RTC::SCTP::OutstandingData::State::ACKED },
});
REQUIRE(retransmissionQueue.ShouldSendForwardTsn(nowMs) == true);
}
SECTION("cwnd recovers when acking")
{
auto retransmissionQueue = createRetransmissionQueue();
constexpr size_t Cwnd{ 1200 };
retransmissionQueue.SetCwnd(Cwnd);
REQUIRE(retransmissionQueue.GetCwnd() == Cwnd);
const std::vector<uint8_t> payload(1000, 0x00);
const size_t chunkSerializedLength = RTC::SCTP::DataChunk::DataChunkHeaderLength + payload.size();
sendQueue
.WillProduceOnce(
[&payload](uint64_t , size_t )
{
return RTC::SCTP::SendQueueInterface::DataToSend(
0, RTC::SCTP::UserData(1, 0, 0, 0, 53, payload, true, true, false));
})
.WillProduceRepeatedly(
[](uint64_t, size_t)
{
return std::nullopt;
});
REQUIRE(getSentPacketTSNs(retransmissionQueue, 1500) == std::vector<uint32_t>{ 10 });
REQUIRE(retransmissionQueue.GetUnackedPacketBytes() == chunkSerializedLength);
retransmissionQueue.HandleReceivedSackChunk(nowMs, createSackChunk(10, Arwnd).get());
REQUIRE(retransmissionQueue.GetCwnd() == Cwnd + chunkSerializedLength);
}
SECTION("can always send one packet")
{
auto retransmissionQueue = createRetransmissionQueue();
const size_t mtu{ Utils::Byte::PadDownTo4Bytes(Mtu) }; const std::vector<uint8_t> payload(mtu - 100, 0x00);
sendQueue
.WillProduceOnce(
[&payload](uint64_t, size_t)
{
return RTC::SCTP::SendQueueInterface::DataToSend(
0, RTC::SCTP::UserData(1, 0, 0, 0, 53, payload, true, false, false));
})
.WillProduceOnce(
[&payload](uint64_t, size_t)
{
return RTC::SCTP::SendQueueInterface::DataToSend(
0, RTC::SCTP::UserData(1, 0, 0, 0, 53, payload, false, false, false));
})
.WillProduceOnce(
[&payload](uint64_t, size_t)
{
return RTC::SCTP::SendQueueInterface::DataToSend(
0, RTC::SCTP::UserData(1, 0, 0, 0, 53, payload, false, false, false));
})
.WillProduceOnce(
[&payload](uint64_t, size_t)
{
return RTC::SCTP::SendQueueInterface::DataToSend(
0, RTC::SCTP::UserData(1, 0, 0, 0, 53, payload, false, false, false));
})
.WillProduceOnce(
[&payload](uint64_t, size_t)
{
return RTC::SCTP::SendQueueInterface::DataToSend(
0, RTC::SCTP::UserData(1, 0, 0, 0, 53, payload, false, true, false));
})
.WillProduceRepeatedly(
[](uint64_t, size_t)
{
return std::nullopt;
});
REQUIRE(
getSentPacketTSNs(retransmissionQueue, 5 * mtu) == std::vector<uint32_t>{ 10, 11, 12, 13, 14 });
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
{ 10, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 11, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 12, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 13, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 14, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
});
retransmissionQueue.HandleReceivedSackChunk(
nowMs,
createSackChunk(
9,
0,
{
{ 3, 3 }
})
.get());
retransmissionQueue.HandleT3RtxTimerExpiry();
REQUIRE(getSentPacketTSNs(retransmissionQueue, mtu) == std::vector<uint32_t>{ 10 });
REQUIRE(getSentPacketTSNs(retransmissionQueue, mtu).empty());
retransmissionQueue.HandleReceivedSackChunk(
nowMs,
createSackChunk(
9,
0,
{
{ 3, 3 }
})
.get());
REQUIRE(retransmissionQueue.GetChunksToSend(nowMs, mtu).empty());
retransmissionQueue.HandleReceivedSackChunk(
nowMs,
createSackChunk(
10,
0,
{
{ 2, 2 }
})
.get());
REQUIRE(getSentPacketTSNs(retransmissionQueue, mtu) == std::vector<uint32_t>{ 11 });
REQUIRE(getSentPacketTSNs(retransmissionQueue, mtu).empty());
retransmissionQueue.HandleReceivedSackChunk(
nowMs, createSackChunk(12, static_cast<uint32_t>(5 * mtu)).get());
REQUIRE(getSentPacketTSNs(retransmissionQueue, mtu) == std::vector<uint32_t>{ 13 });
REQUIRE(getSentPacketTSNs(retransmissionQueue, mtu) == std::vector<uint32_t>{ 14 });
REQUIRE(getSentPacketTSNs(retransmissionQueue, mtu).empty());
}
SECTION("updates rwnd from SACK and unacked payload bytes")
{
auto retransmissionQueue = createRetransmissionQueue();
REQUIRE(retransmissionQueue.GetRwnd() == Arwnd);
constexpr size_t PayloadSize{ 4 };
sendQueue
.WillProduceOnce(createDataToSend(0)) .WillProduceOnce(createDataToSend(1)) .WillProduceOnce(createDataToSend(2)) .WillProduceRepeatedly(
[](uint64_t, size_t)
{
return std::nullopt;
});
REQUIRE(getSentPacketTSNs(retransmissionQueue) == std::vector<uint32_t>{ 10, 11, 12 });
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 9, RTC::SCTP::OutstandingData::State::ACKED },
{ 10, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 11, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 12, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
});
REQUIRE(retransmissionQueue.GetRwnd() == Arwnd - (PayloadSize * 3));
retransmissionQueue.HandleReceivedSackChunk(nowMs, createSackChunk(10, 1000).get());
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 10, RTC::SCTP::OutstandingData::State::ACKED },
{ 11, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
{ 12, RTC::SCTP::OutstandingData::State::IN_FLIGHT },
});
REQUIRE(retransmissionQueue.GetRwnd() == 1000 - (PayloadSize * 2));
retransmissionQueue.HandleReceivedSackChunk(nowMs, createSackChunk(12, 2000).get());
REQUIRE(
retransmissionQueue.GetChunkStatesForTesting() ==
std::vector<std::pair<uint32_t , RTC::SCTP::OutstandingData::State>>{
{ 12, RTC::SCTP::OutstandingData::State::ACKED },
});
REQUIRE(retransmissionQueue.GetRwnd() == 2000);
}
}