#include "handles/BackoffTimerHandleInterface.hpp"
#include "RTC/SCTP/packet/Packet.hpp"
#include "RTC/SCTP/packet/chunks/SackChunk.hpp"
#include "RTC/SCTP/rx/DataTracker.hpp"
#include "test/include/RTC/SCTP/sctpCommon.hpp"
#include "mocks/include/MockShared.hpp"
#include <catch2/catch_test_macros.hpp>
#include <initializer_list>
#include <vector>
SCENARIO("SCTP DataTracker", "[sctp][datatracker]")
{
class MockBackoffTimerHandleListener : public BackoffTimerHandleInterface::Listener
{
public:
void OnBackoffTimer(
BackoffTimerHandleInterface* , uint64_t& , bool& ) override
{
}
};
constexpr uint32_t Arwnd{ 10000 };
constexpr uint64_t Mtu{ 1191 };
constexpr uint32_t InitialTsn{ 11 };
MockBackoffTimerHandleListener backoffTimerHandleListener;
uint64_t nowMs{ 10000 };
mocks::MockShared shared(
[&nowMs]()
{
return nowMs;
});
const std::unique_ptr<BackoffTimerHandleInterface> delayedAckTimerUniquePtr{ shared.CreateBackoffTimer(
BackoffTimerHandleInterface::BackoffTimerHandleOptions{
.listener = std::addressof(backoffTimerHandleListener),
.label = "mock-sctp-delayed-ack",
.baseTimeoutMs = 0,
.backoffAlgorithm = BackoffTimerHandleInterface::BackoffAlgorithm::EXPONENTIAL,
.maxBackoffTimeoutMs = std::nullopt,
.maxRestarts = 0 }) };
auto* delayedAckTimer = delayedAckTimerUniquePtr.get();
RTC::SCTP::DataTracker dataTracker(delayedAckTimer, InitialTsn);
auto createPacket = []()
{
return std::unique_ptr<RTC::SCTP::Packet>{ RTC::SCTP::Packet::Factory(
sctpCommon::FactoryBuffer, Mtu) };
};
auto addSackChunk = [&dataTracker](RTC::SCTP::Packet* packet, size_t aRwnd)
{
dataTracker.AddSackSelectiveAck(packet, aRwnd);
const auto* sackChunk = packet->GetFirstChunkOfType<RTC::SCTP::SackChunk>();
return sackChunk;
};
auto observe = [&dataTracker](std::initializer_list<uint32_t> tsns, bool expectAsDuplicate = false)
{
for (const uint32_t tsn : tsns)
{
if (expectAsDuplicate)
{
REQUIRE(dataTracker.Observe(tsn, false) == false);
}
else
{
REQUIRE(dataTracker.Observe(tsn, false) == true);
}
}
};
SECTION("empty")
{
const auto packet = createPacket();
const auto* sackChunk = addSackChunk(packet.get(), Arwnd);
REQUIRE(sackChunk);
REQUIRE(sackChunk->GetCumulativeTsnAck() == 10);
REQUIRE(sackChunk->GetGapAckBlocks().empty());
REQUIRE(sackChunk->GetDuplicateTsns().empty());
}
SECTION("observe single in order packet")
{
observe({ 11 });
const auto packet = createPacket();
const auto* sackChunk = addSackChunk(packet.get(), Arwnd);
REQUIRE(sackChunk);
REQUIRE(sackChunk->GetCumulativeTsnAck() == 11);
REQUIRE(sackChunk->GetGapAckBlocks().empty());
REQUIRE(sackChunk->GetDuplicateTsns().empty());
}
SECTION("observe many in order moves cumulative tsn ack")
{
observe({ 11, 12, 13 });
const auto packet = createPacket();
const auto* sackChunk = addSackChunk(packet.get(), Arwnd);
REQUIRE(sackChunk);
REQUIRE(sackChunk->GetCumulativeTsnAck() == 13);
REQUIRE(sackChunk->GetGapAckBlocks().empty());
REQUIRE(sackChunk->GetDuplicateTsns().empty());
}
SECTION("observe out of order moves cumulative tsn ack")
{
observe({ 12, 13, 14, 11 });
const auto packet = createPacket();
const auto* sackChunk = addSackChunk(packet.get(), Arwnd);
REQUIRE(sackChunk);
REQUIRE(sackChunk->GetCumulativeTsnAck() == 14);
REQUIRE(sackChunk->GetGapAckBlocks().empty());
REQUIRE(sackChunk->GetDuplicateTsns().empty());
}
SECTION("single gap")
{
observe({ 12 });
const auto packet = createPacket();
const auto* sackChunk = addSackChunk(packet.get(), Arwnd);
REQUIRE(sackChunk);
REQUIRE(sackChunk->GetCumulativeTsnAck() == 10);
REQUIRE(
sackChunk->GetGapAckBlocks() == std::vector<RTC::SCTP::SackChunk::GapAckBlock>{
{ 2, 2 }
});
REQUIRE(sackChunk->GetDuplicateTsns().empty());
}
SECTION("example from RFC 9260 section 3.3.4")
{
observe({ 11, 12, 14, 15, 17 });
const auto packet = createPacket();
const auto* sackChunk = addSackChunk(packet.get(), Arwnd);
REQUIRE(sackChunk);
REQUIRE(sackChunk->GetCumulativeTsnAck() == 12);
REQUIRE(
sackChunk->GetGapAckBlocks() == std::vector<RTC::SCTP::SackChunk::GapAckBlock>{
{ 2, 3 },
{ 5, 5 }
});
REQUIRE(sackChunk->GetDuplicateTsns().empty());
}
SECTION("ack already received chunks")
{
observe({ 11 });
const auto packet1 = createPacket();
const auto* sackChunk1 = addSackChunk(packet1.get(), Arwnd);
REQUIRE(sackChunk1);
REQUIRE(sackChunk1->GetCumulativeTsnAck() == 11);
REQUIRE(sackChunk1->GetGapAckBlocks().empty());
observe({ 8 }, true);
const auto packet2 = createPacket();
const auto* sackChunk2 = addSackChunk(packet2.get(), Arwnd);
REQUIRE(sackChunk2);
REQUIRE(sackChunk2->GetCumulativeTsnAck() == 11);
REQUIRE(sackChunk2->GetGapAckBlocks().empty());
}
SECTION("double send retransmitted chunk")
{
observe({ 11, 13, 14, 15 });
const auto packet1 = createPacket();
const auto* sackChunk1 = addSackChunk(packet1.get(), Arwnd);
REQUIRE(sackChunk1);
REQUIRE(sackChunk1->GetCumulativeTsnAck() == 11);
REQUIRE(
sackChunk1->GetGapAckBlocks() == std::vector<RTC::SCTP::SackChunk::GapAckBlock>{
{ 2, 4 },
});
observe({ 12, 16, 17, 18 });
const auto packet2 = createPacket();
const auto* sackChunk2 = addSackChunk(packet2.get(), Arwnd);
REQUIRE(sackChunk2);
REQUIRE(sackChunk2->GetCumulativeTsnAck() == 18);
REQUIRE(sackChunk2->GetGapAckBlocks().empty());
observe({ 12 }, true);
observe({ 19, 20, 21 });
const auto packet3 = createPacket();
const auto* sackChunk3 = addSackChunk(packet3.get(), Arwnd);
REQUIRE(sackChunk3);
REQUIRE(sackChunk3->GetCumulativeTsnAck() == 21);
REQUIRE(sackChunk3->GetGapAckBlocks().empty());
}
SECTION("forward tsn simple")
{
observe({ 11, 12, 15 });
dataTracker.HandleForwardTsn(13);
const auto packet = createPacket();
const auto* sackChunk = addSackChunk(packet.get(), Arwnd);
REQUIRE(sackChunk);
REQUIRE(sackChunk->GetCumulativeTsnAck() == 13);
REQUIRE(
sackChunk->GetGapAckBlocks() == std::vector<RTC::SCTP::SackChunk::GapAckBlock>{
{ 2, 2 },
});
REQUIRE(sackChunk->GetDuplicateTsns().empty());
}
SECTION("forward tsn skips from gap block")
{
observe({ 11, 12, 14 });
dataTracker.HandleForwardTsn(13);
const auto packet = createPacket();
const auto* sackChunk = addSackChunk(packet.get(), Arwnd);
REQUIRE(sackChunk);
REQUIRE(sackChunk->GetCumulativeTsnAck() == 14);
REQUIRE(sackChunk->GetGapAckBlocks().empty());
REQUIRE(sackChunk->GetDuplicateTsns().empty());
}
SECTION("example from RFC 3758")
{
dataTracker.HandleForwardTsn(102);
observe({ 102 }, true);
observe({ 104, 105, 107 });
dataTracker.HandleForwardTsn(103);
const auto packet = createPacket();
const auto* sackChunk = addSackChunk(packet.get(), Arwnd);
REQUIRE(sackChunk);
REQUIRE(sackChunk->GetCumulativeTsnAck() == 105);
REQUIRE(
sackChunk->GetGapAckBlocks() == std::vector<RTC::SCTP::SackChunk::GapAckBlock>{
{ 2, 2 },
});
}
SECTION("empty all acks")
{
observe({ 11, 13, 14, 15 });
dataTracker.HandleForwardTsn(100);
const auto packet = createPacket();
const auto* sackChunk = addSackChunk(packet.get(), Arwnd);
REQUIRE(sackChunk);
REQUIRE(sackChunk->GetCumulativeTsnAck() == 100);
REQUIRE(sackChunk->GetGapAckBlocks().empty());
REQUIRE(sackChunk->GetDuplicateTsns().empty());
}
SECTION("sets arwnd correctly")
{
const auto packet1 = createPacket();
const auto* sackChunk1 = addSackChunk(packet1.get(), 100);
REQUIRE(sackChunk1);
REQUIRE(sackChunk1->GetAdvertisedReceiverWindowCredit() == 100);
const auto packet2 = createPacket();
const auto* sackChunk2 = addSackChunk(packet2.get(), 101);
REQUIRE(sackChunk2);
REQUIRE(sackChunk2->GetAdvertisedReceiverWindowCredit() == 101);
}
SECTION("will increase cumulative ack tsn")
{
REQUIRE(dataTracker.GetLastCumulativeAckedTsn() == 10);
REQUIRE(dataTracker.WillIncreaseCumAckTsn(10) == false);
REQUIRE(dataTracker.WillIncreaseCumAckTsn(11) == true);
REQUIRE(dataTracker.WillIncreaseCumAckTsn(12) == false);
observe({ 11, 12, 13, 14, 15 });
REQUIRE(dataTracker.GetLastCumulativeAckedTsn() == 15);
REQUIRE(dataTracker.WillIncreaseCumAckTsn(15) == false);
REQUIRE(dataTracker.WillIncreaseCumAckTsn(16) == true);
REQUIRE(dataTracker.WillIncreaseCumAckTsn(17) == false);
}
SECTION("force should send sack immediately")
{
REQUIRE(dataTracker.ShouldSendAck() == false);
dataTracker.ForceImmediateSack();
REQUIRE(dataTracker.ShouldSendAck() == true);
}
SECTION("will accept valid tsns")
{
const uint32_t lastTsn = InitialTsn - 1;
const int limit = static_cast<int>(RTC::SCTP::DataTracker::MaxAcceptedOutstandingFragments);
for (int i{ -limit }; i <= limit; ++i)
{
REQUIRE(dataTracker.IsTsnValid(lastTsn + i) == true);
}
}
SECTION("will not accept invalid tsns")
{
const uint32_t lastTsn = InitialTsn - 1;
const uint32_t limit = RTC::SCTP::DataTracker::MaxAcceptedOutstandingFragments;
REQUIRE(dataTracker.IsTsnValid(lastTsn + limit + 1) == false);
REQUIRE(dataTracker.IsTsnValid(lastTsn - (limit + 1)) == false);
REQUIRE(dataTracker.IsTsnValid(lastTsn + 0x8000000) == false);
REQUIRE(dataTracker.IsTsnValid(lastTsn - 0x8000000) == false);
}
SECTION("report single duplicate tsns")
{
observe({ 11, 12 });
observe({ 11 }, true);
const auto packet = createPacket();
const auto* sackChunk = addSackChunk(packet.get(), Arwnd);
REQUIRE(sackChunk);
REQUIRE(sackChunk->GetCumulativeTsnAck() == 12);
REQUIRE(sackChunk->GetGapAckBlocks().empty());
REQUIRE(sackChunk->GetDuplicateTsns() == std::vector<uint32_t>{ 11 });
}
SECTION("report multiple duplicate tsns")
{
observe({ 11, 12, 13, 14 });
observe({ 12, 13, 12, 13 }, true);
observe({ 15, 16 });
const auto packet = createPacket();
const auto* sackChunk = addSackChunk(packet.get(), Arwnd);
REQUIRE(sackChunk);
REQUIRE(sackChunk->GetCumulativeTsnAck() == 16);
REQUIRE(sackChunk->GetGapAckBlocks().empty());
REQUIRE(sackChunk->GetDuplicateTsns() == std::vector<uint32_t>{ 12, 13 });
}
SECTION("report duplicate tsns in gap-ack-blocks")
{
observe({ 11, 13, 14 });
observe({ 13, 14 }, true);
observe({ 15, 16 });
const auto packet = createPacket();
const auto* sackChunk = addSackChunk(packet.get(), Arwnd);
REQUIRE(sackChunk);
REQUIRE(sackChunk->GetCumulativeTsnAck() == 11);
REQUIRE(
sackChunk->GetGapAckBlocks() == std::vector<RTC::SCTP::SackChunk::GapAckBlock>{
{ 2, 5 },
});
REQUIRE(sackChunk->GetDuplicateTsns() == std::vector<uint32_t>{ 13, 14 });
}
SECTION("clears duplicate tsns after creating sack")
{
observe({ 11, 12, 13, 14 });
observe({ 12, 13, 12, 13 }, true);
observe({ 15, 16 });
const auto packet1 = createPacket();
const auto* sackChunk1 = addSackChunk(packet1.get(), Arwnd);
REQUIRE(sackChunk1);
REQUIRE(sackChunk1->GetCumulativeTsnAck() == 16);
REQUIRE(sackChunk1->GetGapAckBlocks().empty());
REQUIRE(sackChunk1->GetDuplicateTsns() == std::vector<uint32_t>{ 12, 13 });
observe({ 17 });
const auto packet2 = createPacket();
const auto* sackChunk2 = addSackChunk(packet2.get(), Arwnd);
REQUIRE(sackChunk2);
REQUIRE(sackChunk2->GetCumulativeTsnAck() == 17);
REQUIRE(sackChunk2->GetGapAckBlocks().empty());
REQUIRE(sackChunk2->GetDuplicateTsns().empty());
}
SECTION("limits number of duplicates reported")
{
for (size_t i{ 0 }; i < RTC::SCTP::DataTracker::MaxDuplicateTsnReported + 10; ++i)
{
const uint32_t tsn = 11 + static_cast<uint32_t>(i);
dataTracker.Observe(tsn, false);
dataTracker.Observe(tsn, false);
}
const auto packet = createPacket();
const auto* sackChunk = addSackChunk(packet.get(), Arwnd);
REQUIRE(sackChunk);
REQUIRE(sackChunk->GetGapAckBlocks().empty());
REQUIRE(sackChunk->GetDuplicateTsns().size() == RTC::SCTP::DataTracker::MaxDuplicateTsnReported);
}
SECTION("limits number of gap-ack-blocks reported")
{
for (size_t i{ 0 }; i < RTC::SCTP::DataTracker::MaxGapAckBlocksReported + 10; ++i)
{
const uint32_t tsn = 11 + static_cast<uint32_t>(i * 2);
dataTracker.Observe(tsn, false);
}
const auto packet = createPacket();
const auto* sackChunk = addSackChunk(packet.get(), Arwnd);
REQUIRE(sackChunk);
REQUIRE(sackChunk->GetCumulativeTsnAck() == 11);
REQUIRE(sackChunk->GetGapAckBlocks().size() == RTC::SCTP::DataTracker::MaxGapAckBlocksReported);
}
SECTION("sends sack for first packet observed")
{
observe({ 11 });
dataTracker.ObservePacketEnd();
REQUIRE(dataTracker.ShouldSendAck() == true);
const auto* backoffTimer = shared.GetBackoffTimer("mock-sctp-delayed-ack");
REQUIRE(backoffTimer);
REQUIRE(backoffTimer->IsRunning() == false);
}
SECTION("sends sack every second packet when there is no packet loss")
{
auto* backoffTimer = shared.GetBackoffTimer("mock-sctp-delayed-ack");
REQUIRE(backoffTimer);
observe({ 11 });
dataTracker.ObservePacketEnd();
REQUIRE(dataTracker.ShouldSendAck() == true);
REQUIRE(backoffTimer->IsRunning() == false);
observe({ 12 });
dataTracker.ObservePacketEnd();
REQUIRE(dataTracker.ShouldSendAck() == false);
REQUIRE(backoffTimer->IsRunning() == true);
observe({ 13 });
dataTracker.ObservePacketEnd();
REQUIRE(dataTracker.ShouldSendAck() == true);
REQUIRE(backoffTimer->IsRunning() == false);
observe({ 14 });
dataTracker.ObservePacketEnd();
REQUIRE(dataTracker.ShouldSendAck() == false);
REQUIRE(backoffTimer->IsRunning() == true);
observe({ 15 });
dataTracker.ObservePacketEnd();
REQUIRE(dataTracker.ShouldSendAck() == true);
REQUIRE(backoffTimer->IsRunning() == false);
}
SECTION("sends sack every packet on packet loss")
{
const auto* backoffTimer = shared.GetBackoffTimer("mock-sctp-delayed-ack");
REQUIRE(backoffTimer);
observe({ 11 });
dataTracker.ObservePacketEnd();
REQUIRE(dataTracker.ShouldSendAck() == true);
REQUIRE(backoffTimer->IsRunning() == false);
observe({ 13 });
dataTracker.ObservePacketEnd();
REQUIRE(dataTracker.ShouldSendAck() == true);
REQUIRE(backoffTimer->IsRunning() == false);
observe({ 14 });
dataTracker.ObservePacketEnd();
REQUIRE(dataTracker.ShouldSendAck() == true);
REQUIRE(backoffTimer->IsRunning() == false);
observe({ 15 });
dataTracker.ObservePacketEnd();
REQUIRE(dataTracker.ShouldSendAck() == true);
REQUIRE(backoffTimer->IsRunning() == false);
observe({ 16 });
dataTracker.ObservePacketEnd();
REQUIRE(dataTracker.ShouldSendAck() == true);
REQUIRE(backoffTimer->IsRunning() == false);
observe({ 12 });
dataTracker.ObservePacketEnd();
REQUIRE(dataTracker.ShouldSendAck() == false);
REQUIRE(backoffTimer->IsRunning() == true);
observe({ 17 });
dataTracker.ObservePacketEnd();
REQUIRE(dataTracker.ShouldSendAck() == true);
REQUIRE(backoffTimer->IsRunning() == false);
observe({ 18 });
dataTracker.ObservePacketEnd();
REQUIRE(dataTracker.ShouldSendAck() == false);
REQUIRE(backoffTimer->IsRunning() == true);
}
SECTION("sends sack on duplicate data chunks")
{
const auto* backoffTimer = shared.GetBackoffTimer("mock-sctp-delayed-ack");
REQUIRE(backoffTimer);
observe({ 11 });
dataTracker.ObservePacketEnd();
REQUIRE(dataTracker.ShouldSendAck() == true);
REQUIRE(backoffTimer->IsRunning() == false);
observe({ 11 }, true);
dataTracker.ObservePacketEnd();
REQUIRE(dataTracker.ShouldSendAck() == true);
REQUIRE(backoffTimer->IsRunning() == false);
observe({ 12 });
dataTracker.ObservePacketEnd();
REQUIRE(dataTracker.ShouldSendAck() == false);
REQUIRE(backoffTimer->IsRunning() == true);
observe({ 13 });
dataTracker.ObservePacketEnd();
REQUIRE(dataTracker.ShouldSendAck() == true);
REQUIRE(backoffTimer->IsRunning() == false);
observe({ 12 }, true);
dataTracker.ObservePacketEnd();
REQUIRE(dataTracker.ShouldSendAck() == true);
REQUIRE(backoffTimer->IsRunning() == false);
}
SECTION("gap-ack-block add single block")
{
observe({ 12 });
const auto packet = createPacket();
const auto* sackChunk = addSackChunk(packet.get(), Arwnd);
REQUIRE(sackChunk);
REQUIRE(sackChunk->GetCumulativeTsnAck() == 10);
REQUIRE(
sackChunk->GetGapAckBlocks() == std::vector<RTC::SCTP::SackChunk::GapAckBlock>{
{ 2, 2 },
});
}
SECTION("gap-ack-block adds another")
{
observe({ 12 });
observe({ 14 });
const auto packet = createPacket();
const auto* sackChunk = addSackChunk(packet.get(), Arwnd);
REQUIRE(sackChunk);
REQUIRE(sackChunk->GetCumulativeTsnAck() == 10);
REQUIRE(
sackChunk->GetGapAckBlocks() == std::vector<RTC::SCTP::SackChunk::GapAckBlock>{
{ 2, 2 },
{ 4, 4 },
});
}
SECTION("gap-ack-block adds duplicate")
{
observe({ 12 });
observe({ 12 }, true);
const auto packet = createPacket();
const auto* sackChunk = addSackChunk(packet.get(), Arwnd);
REQUIRE(sackChunk);
REQUIRE(sackChunk->GetCumulativeTsnAck() == 10);
REQUIRE(
sackChunk->GetGapAckBlocks() == std::vector<RTC::SCTP::SackChunk::GapAckBlock>{
{ 2, 2 },
});
REQUIRE(sackChunk->GetDuplicateTsns() == std::vector<uint32_t>{ 12 });
}
SECTION("gap-ack-block expands to right")
{
observe({ 12 });
observe({ 13 });
const auto packet = createPacket();
const auto* sackChunk = addSackChunk(packet.get(), Arwnd);
REQUIRE(sackChunk);
REQUIRE(sackChunk->GetCumulativeTsnAck() == 10);
REQUIRE(
sackChunk->GetGapAckBlocks() == std::vector<RTC::SCTP::SackChunk::GapAckBlock>{
{ 2, 3 },
});
}
SECTION("gap-ack-block expands to right with other")
{
observe({ 12 });
observe({ 20 });
observe({ 30 });
observe({ 21 });
const auto packet = createPacket();
const auto* sackChunk = addSackChunk(packet.get(), Arwnd);
REQUIRE(sackChunk);
REQUIRE(sackChunk->GetCumulativeTsnAck() == 10);
REQUIRE(
sackChunk->GetGapAckBlocks() == std::vector<RTC::SCTP::SackChunk::GapAckBlock>{
{ 2, 2 },
{ 10, 11 },
{ 20, 20 },
});
}
SECTION("gap-ack-block expands to left")
{
observe({ 13 });
observe({ 12 });
const auto packet = createPacket();
const auto* sackChunk = addSackChunk(packet.get(), Arwnd);
REQUIRE(sackChunk);
REQUIRE(sackChunk->GetCumulativeTsnAck() == 10);
REQUIRE(
sackChunk->GetGapAckBlocks() == std::vector<RTC::SCTP::SackChunk::GapAckBlock>{
{ 2, 3 },
});
}
SECTION("gap-ack-block expands to left with other")
{
observe({ 12 });
observe({ 21 });
observe({ 30 });
observe({ 20 });
const auto packet = createPacket();
const auto* sackChunk = addSackChunk(packet.get(), Arwnd);
REQUIRE(sackChunk);
REQUIRE(sackChunk->GetCumulativeTsnAck() == 10);
REQUIRE(
sackChunk->GetGapAckBlocks() == std::vector<RTC::SCTP::SackChunk::GapAckBlock>{
{ 2, 2 },
{ 10, 11 },
{ 20, 20 },
});
}
SECTION("gap-ack-block expands to right and merges")
{
observe({ 12 });
observe({ 20 });
observe({ 22 });
observe({ 30 });
observe({ 21 });
const auto packet = createPacket();
const auto* sackChunk = addSackChunk(packet.get(), Arwnd);
REQUIRE(sackChunk);
REQUIRE(sackChunk->GetCumulativeTsnAck() == 10);
REQUIRE(
sackChunk->GetGapAckBlocks() == std::vector<RTC::SCTP::SackChunk::GapAckBlock>{
{ 2, 2 },
{ 10, 12 },
{ 20, 20 },
});
}
SECTION("gap-ack-block merges many blocks into one")
{
auto getGapAckBlocks = [&]()
{
const auto packet = createPacket();
const auto* sackChunk = addSackChunk(packet.get(), Arwnd);
REQUIRE(sackChunk);
return sackChunk->GetGapAckBlocks();
};
observe({ 22 });
REQUIRE(
getGapAckBlocks() == std::vector<RTC::SCTP::SackChunk::GapAckBlock>{
{ 12, 12 }
});
observe({ 30 });
REQUIRE(
getGapAckBlocks() == std::vector<RTC::SCTP::SackChunk::GapAckBlock>{
{ 12, 12 },
{ 20, 20 },
});
observe({ 24 });
REQUIRE(
getGapAckBlocks() == std::vector<RTC::SCTP::SackChunk::GapAckBlock>{
{ 12, 12 },
{ 14, 14 },
{ 20, 20 },
});
observe({ 28 });
REQUIRE(
getGapAckBlocks() == std::vector<RTC::SCTP::SackChunk::GapAckBlock>{
{ 12, 12 },
{ 14, 14 },
{ 18, 18 },
{ 20, 20 },
});
observe({ 26 });
REQUIRE(
getGapAckBlocks() == std::vector<RTC::SCTP::SackChunk::GapAckBlock>{
{ 12, 12 },
{ 14, 14 },
{ 16, 16 },
{ 18, 18 },
{ 20, 20 },
});
observe({ 29 });
REQUIRE(
getGapAckBlocks() == std::vector<RTC::SCTP::SackChunk::GapAckBlock>{
{ 12, 12 },
{ 14, 14 },
{ 16, 16 },
{ 18, 20 },
});
observe({ 23 });
REQUIRE(
getGapAckBlocks() == std::vector<RTC::SCTP::SackChunk::GapAckBlock>{
{ 12, 14 },
{ 16, 16 },
{ 18, 20 },
});
observe({ 27 });
REQUIRE(
getGapAckBlocks() == std::vector<RTC::SCTP::SackChunk::GapAckBlock>{
{ 12, 14 },
{ 16, 20 },
});
observe({ 25 });
REQUIRE(
getGapAckBlocks() == std::vector<RTC::SCTP::SackChunk::GapAckBlock>{
{ 12, 20 }
});
observe({ 20 });
REQUIRE(
getGapAckBlocks() == std::vector<RTC::SCTP::SackChunk::GapAckBlock>{
{ 10, 10 },
{ 12, 20 },
});
observe({ 32 });
REQUIRE(
getGapAckBlocks() == std::vector<RTC::SCTP::SackChunk::GapAckBlock>{
{ 10, 10 },
{ 12, 20 },
{ 22, 22 },
});
observe({ 21 });
REQUIRE(
getGapAckBlocks() == std::vector<RTC::SCTP::SackChunk::GapAckBlock>{
{ 10, 20 },
{ 22, 22 },
});
observe({ 31 });
REQUIRE(
getGapAckBlocks() == std::vector<RTC::SCTP::SackChunk::GapAckBlock>{
{ 10, 22 }
});
}
SECTION("gap-ack-block remove before cumulative ack tsn")
{
observe({ 12, 13, 14, 20, 21, 22, 30, 31 });
dataTracker.HandleForwardTsn(8);
const auto packet = createPacket();
const auto* sackChunk = addSackChunk(packet.get(), Arwnd);
REQUIRE(sackChunk);
REQUIRE(sackChunk->GetCumulativeTsnAck() == 10);
REQUIRE(
sackChunk->GetGapAckBlocks() == std::vector<RTC::SCTP::SackChunk::GapAckBlock>{
{ 2, 4 },
{ 10, 12 },
{ 20, 21 },
});
}
SECTION("gap-ack-block remove before first block")
{
observe({ 12, 13, 14, 20, 21, 22, 30, 31 });
dataTracker.HandleForwardTsn(11);
const auto packet = createPacket();
const auto* sackChunk = addSackChunk(packet.get(), Arwnd);
REQUIRE(sackChunk);
REQUIRE(sackChunk->GetCumulativeTsnAck() == 14);
REQUIRE(
sackChunk->GetGapAckBlocks() == std::vector<RTC::SCTP::SackChunk::GapAckBlock>{
{ 6, 8 },
{ 16, 17 },
});
}
SECTION("gap-ack-block remove at beginning of first block")
{
observe({ 12, 13, 14, 20, 21, 22, 30, 31 });
dataTracker.HandleForwardTsn(12);
const auto packet = createPacket();
const auto* sackChunk = addSackChunk(packet.get(), Arwnd);
REQUIRE(sackChunk);
REQUIRE(sackChunk->GetCumulativeTsnAck() == 14);
REQUIRE(
sackChunk->GetGapAckBlocks() == std::vector<RTC::SCTP::SackChunk::GapAckBlock>{
{ 6, 8 },
{ 16, 17 },
});
}
SECTION("gap-ack-block remove at middle of first block")
{
observe({ 12, 13, 14, 20, 21, 22, 30, 31 });
dataTracker.HandleForwardTsn(13);
const auto packet = createPacket();
const auto* sackChunk = addSackChunk(packet.get(), Arwnd);
REQUIRE(sackChunk);
REQUIRE(sackChunk->GetCumulativeTsnAck() == 14);
REQUIRE(
sackChunk->GetGapAckBlocks() == std::vector<RTC::SCTP::SackChunk::GapAckBlock>{
{ 6, 8 },
{ 16, 17 },
});
}
SECTION("gap-ack-block remove at end of first block")
{
observe({ 12, 13, 14, 20, 21, 22, 30, 31 });
dataTracker.HandleForwardTsn(14);
const auto packet = createPacket();
const auto* sackChunk = addSackChunk(packet.get(), Arwnd);
REQUIRE(sackChunk);
REQUIRE(sackChunk->GetCumulativeTsnAck() == 14);
REQUIRE(
sackChunk->GetGapAckBlocks() == std::vector<RTC::SCTP::SackChunk::GapAckBlock>{
{ 6, 8 },
{ 16, 17 },
});
}
SECTION("gap-ack-block remove right after first block")
{
observe({ 12, 13, 14, 20, 21, 22, 30, 31 });
dataTracker.HandleForwardTsn(18);
const auto packet = createPacket();
const auto* sackChunk = addSackChunk(packet.get(), Arwnd);
REQUIRE(sackChunk);
REQUIRE(sackChunk->GetCumulativeTsnAck() == 18);
REQUIRE(
sackChunk->GetGapAckBlocks() == std::vector<RTC::SCTP::SackChunk::GapAckBlock>{
{ 2, 4 },
{ 12, 13 },
});
}
SECTION("gap-ack-block remove right before second block")
{
observe({ 12, 13, 14, 20, 21, 22, 30, 31 });
dataTracker.HandleForwardTsn(19);
const auto packet = createPacket();
const auto* sackChunk = addSackChunk(packet.get(), Arwnd);
REQUIRE(sackChunk);
REQUIRE(sackChunk->GetCumulativeTsnAck() == 22);
REQUIRE(
sackChunk->GetGapAckBlocks() == std::vector<RTC::SCTP::SackChunk::GapAckBlock>{
{ 8, 9 },
});
}
SECTION("gap-ack-block remove right at start of second block")
{
observe({ 12, 13, 14, 20, 21, 22, 30, 31 });
dataTracker.HandleForwardTsn(20);
const auto packet = createPacket();
const auto* sackChunk = addSackChunk(packet.get(), Arwnd);
REQUIRE(sackChunk);
REQUIRE(sackChunk->GetCumulativeTsnAck() == 22);
REQUIRE(
sackChunk->GetGapAckBlocks() == std::vector<RTC::SCTP::SackChunk::GapAckBlock>{
{ 8, 9 },
});
}
SECTION("gap-ack-block remove right at middle of second block")
{
observe({ 12, 13, 14, 20, 21, 22, 30, 31 });
dataTracker.HandleForwardTsn(21);
const auto packet = createPacket();
const auto* sackChunk = addSackChunk(packet.get(), Arwnd);
REQUIRE(sackChunk);
REQUIRE(sackChunk->GetCumulativeTsnAck() == 22);
REQUIRE(
sackChunk->GetGapAckBlocks() == std::vector<RTC::SCTP::SackChunk::GapAckBlock>{
{ 8, 9 },
});
}
SECTION("gap-ack-block remove right at end of second block")
{
observe({ 12, 13, 14, 20, 21, 22, 30, 31 });
dataTracker.HandleForwardTsn(22);
const auto packet = createPacket();
const auto* sackChunk = addSackChunk(packet.get(), Arwnd);
REQUIRE(sackChunk);
REQUIRE(sackChunk->GetCumulativeTsnAck() == 22);
REQUIRE(
sackChunk->GetGapAckBlocks() == std::vector<RTC::SCTP::SackChunk::GapAckBlock>{
{ 8, 9 },
});
}
SECTION("gap-ack-block remove far after all blocks")
{
observe({ 12, 13, 14, 20, 21, 22, 30, 31 });
dataTracker.HandleForwardTsn(40);
const auto packet = createPacket();
const auto* sackChunk = addSackChunk(packet.get(), Arwnd);
REQUIRE(sackChunk);
REQUIRE(sackChunk->GetCumulativeTsnAck() == 40);
REQUIRE(sackChunk->GetGapAckBlocks().empty());
}
SECTION("does not accept data before forward tsn")
{
observe({ 12, 13, 14, 15, 17 });
dataTracker.ObservePacketEnd();
dataTracker.HandleForwardTsn(13);
REQUIRE(dataTracker.Observe(11, false) == false);
}
SECTION("does not accept data at forward tsn")
{
observe({ 12, 13, 14, 15, 17 });
dataTracker.ObservePacketEnd();
dataTracker.HandleForwardTsn(16);
REQUIRE(dataTracker.Observe(16, false) == false);
}
SECTION("does not accept data before cumulative ack tsn")
{
REQUIRE(InitialTsn == 11);
REQUIRE(dataTracker.Observe(10, false) == false);
}
SECTION("does not accept contiguous duplicate data")
{
REQUIRE(InitialTsn == 11);
REQUIRE(dataTracker.Observe(11, false) == true);
REQUIRE(dataTracker.Observe(11, false) == false);
REQUIRE(dataTracker.Observe(12, false) == true);
REQUIRE(dataTracker.Observe(12, false) == false);
REQUIRE(dataTracker.Observe(11, false) == false);
REQUIRE(dataTracker.Observe(10, false) == false);
}
SECTION("does not accept gaps with duplicate data")
{
REQUIRE(InitialTsn == 11);
REQUIRE(dataTracker.Observe(11, false) == true);
REQUIRE(dataTracker.Observe(11, false) == false);
REQUIRE(dataTracker.Observe(14, false) == true);
REQUIRE(dataTracker.Observe(14, false) == false);
REQUIRE(dataTracker.Observe(13, false) == true);
REQUIRE(dataTracker.Observe(13, false) == false);
REQUIRE(dataTracker.Observe(12, false) == true);
REQUIRE(dataTracker.Observe(12, false) == false);
}
}