#include "common.hpp"
#include "RTC/SCTP/packet/UserData.hpp"
#include "RTC/SCTP/tx/SendQueueInterface.hpp"
#include "RTC/SCTP/tx/StreamScheduler.hpp"
#include <catch2/catch_test_macros.hpp>
#include <deque>
#include <map>
#include <vector>
namespace
{
constexpr uint64_t Mtu{ 1000 };
constexpr size_t PayloadLength{ 4 };
constexpr uint64_t NowMs{ 0 };
bool checkDataToSendHasMid(
std::optional<RTC::SCTP::SendQueueInterface::DataToSend> dataToSend, uint32_t mid)
{
if (!dataToSend.has_value())
{
return false;
}
if (dataToSend->data.GetMessageId() != mid)
{
return false;
}
return true;
}
std::function<std::optional<RTC::SCTP::SendQueueInterface::DataToSend>(uint64_t, size_t)> createChunk(
uint32_t outgoingMessageId, uint16_t streamId, uint32_t mid, size_t payloadLength = PayloadLength)
{
return
[streamId, mid, payloadLength, outgoingMessageId](uint64_t , size_t )
{
return RTC::SCTP::SendQueueInterface::DataToSend(
outgoingMessageId,
RTC::SCTP::UserData(
streamId,
0,
mid,
0,
42,
std::vector<uint8_t>(payloadLength),
true,
true,
true));
};
}
std::map<uint16_t, size_t> getPacketCounts(
RTC::SCTP::StreamScheduler& scheduler, size_t packetsToGenerate)
{
std::map< uint16_t, size_t> packetCounts;
for (size_t i{ 0 }; i < packetsToGenerate; ++i)
{
const std::optional<RTC::SCTP::SendQueueInterface::DataToSend> dataToSend =
scheduler.Produce(NowMs, Mtu);
if (dataToSend.has_value())
{
++packetCounts[dataToSend->data.GetStreamId()];
}
}
return packetCounts;
}
class MockStreamProducer : public RTC::SCTP::StreamScheduler::StreamProducer
{
public:
void PushProduce(
std::function<std::optional<RTC::SCTP::SendQueueInterface::DataToSend>(uint64_t, size_t)> fn)
{
this->produceQueue.push_back(std::move(fn));
}
void PushBytesToSend(size_t bytes)
{
this->bytesQueue.push_back(bytes);
}
std::optional<RTC::SCTP::SendQueueInterface::DataToSend> Produce(uint64_t nowMs, size_t maxLength) override
{
REQUIRE(!this->produceQueue.empty());
const auto fn = std::move(this->produceQueue.front());
this->produceQueue.pop_front();
return fn(nowMs, maxLength);
}
size_t GetBytesToSendInNextMessage() const override
{
REQUIRE(!this->bytesQueue.empty());
const size_t bytes = this->bytesQueue.front();
this->bytesQueue.pop_front();
return bytes;
}
private:
std::deque<std::function<std::optional<RTC::SCTP::SendQueueInterface::DataToSend>(uint64_t, size_t)>>
produceQueue;
mutable std::deque<size_t> bytesQueue;
};
class TestStream
{
public:
TestStream(
RTC::SCTP::StreamScheduler& scheduler,
uint16_t streamId,
uint16_t priority,
size_t packetLength = PayloadLength)
{
this->producer.PushBytesToSend(packetLength);
for (int i{ 0 }; i < 100; ++i)
{
this->producer.PushProduce(createChunk(i, streamId, i, packetLength));
this->producer.PushBytesToSend(packetLength);
}
this->stream = scheduler.CreateStream(std::addressof(producer), streamId, priority);
this->stream->MayMakeActive();
}
RTC::SCTP::StreamScheduler::Stream& GetStream()
{
return *stream;
}
private:
MockStreamProducer producer;
std::unique_ptr<RTC::SCTP::StreamScheduler::Stream> stream;
};
}
SCENARIO("SCTP StreamScheduler", "[sctp][streamscheduler]")
{
SECTION("has no active streams")
{
RTC::SCTP::StreamScheduler scheduler(Mtu);
REQUIRE(!scheduler.Produce(NowMs, Mtu).has_value());
}
SECTION("can set and get stream properties")
{
RTC::SCTP::StreamScheduler scheduler(Mtu);
MockStreamProducer producer;
auto stream = scheduler.CreateStream(std::addressof(producer), 1, 2);
REQUIRE(stream->GetStreamId() == 1);
REQUIRE(stream->GetPriority() == 2);
stream->SetPriority(0);
REQUIRE(stream->GetPriority() == 0);
}
SECTION("can produce from a single stream")
{
RTC::SCTP::StreamScheduler scheduler(Mtu);
MockStreamProducer producer;
producer.PushBytesToSend(PayloadLength);
producer.PushProduce(createChunk(0, 1, 0));
producer.PushBytesToSend(0);
auto stream = scheduler.CreateStream(std::addressof(producer), 1, 2);
stream->MayMakeActive();
REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 0));
REQUIRE(!scheduler.Produce(NowMs, Mtu).has_value());
}
SECTION("will round-robin between streams")
{
RTC::SCTP::StreamScheduler scheduler(Mtu);
MockStreamProducer producer1;
producer1.PushBytesToSend(PayloadLength);
producer1.PushProduce(createChunk(0, 1, 100));
producer1.PushBytesToSend(PayloadLength);
producer1.PushProduce(createChunk(1, 1, 101));
producer1.PushBytesToSend(PayloadLength);
producer1.PushProduce(createChunk(2, 1, 102));
producer1.PushBytesToSend(0);
auto stream1 = scheduler.CreateStream(std::addressof(producer1), 1, 2);
stream1->MayMakeActive();
MockStreamProducer producer2;
producer2.PushBytesToSend(PayloadLength);
producer2.PushProduce(createChunk(3, 2, 200));
producer2.PushBytesToSend(PayloadLength);
producer2.PushProduce(createChunk(4, 2, 201));
producer2.PushBytesToSend(PayloadLength);
producer2.PushProduce(createChunk(5, 2, 202));
producer2.PushBytesToSend(0);
auto stream2 = scheduler.CreateStream(std::addressof(producer2), 2, 2);
stream2->MayMakeActive();
REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 100));
REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 200));
REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 101));
REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 201));
REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 102));
REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 202));
REQUIRE(!scheduler.Produce(NowMs, Mtu).has_value());
}
SECTION("will round-robin between streams")
{
RTC::SCTP::StreamScheduler scheduler(Mtu);
MockStreamProducer producer1;
producer1.PushBytesToSend(PayloadLength);
producer1.PushProduce(createChunk(0, 1, 100));
producer1.PushBytesToSend(PayloadLength);
producer1.PushProduce(createChunk(1, 1, 101));
producer1.PushBytesToSend(PayloadLength);
producer1.PushProduce(createChunk(2, 1, 102));
producer1.PushBytesToSend(0);
auto stream1 = scheduler.CreateStream(std::addressof(producer1), 1, 2);
stream1->MayMakeActive();
MockStreamProducer producer2;
producer2.PushBytesToSend(PayloadLength);
producer2.PushProduce(createChunk(3, 2, 200));
producer2.PushBytesToSend(PayloadLength);
producer2.PushProduce(createChunk(4, 2, 201));
producer2.PushBytesToSend(PayloadLength);
producer2.PushProduce(createChunk(5, 2, 202));
producer2.PushBytesToSend(0);
auto stream2 = scheduler.CreateStream(std::addressof(producer2), 2, 2);
stream2->MayMakeActive();
REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 100));
REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 200));
REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 101));
REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 201));
REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 102));
REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 202));
REQUIRE(!scheduler.Produce(NowMs, Mtu).has_value());
}
SECTION("will round-robin only when finished producing chunk")
{
RTC::SCTP::StreamScheduler scheduler(Mtu);
MockStreamProducer producer1;
producer1.PushBytesToSend(PayloadLength); producer1.PushProduce(createChunk(0, 1, 100));
producer1.PushBytesToSend(PayloadLength);
producer1.PushProduce(
[](uint64_t, size_t)
{
return RTC::SCTP::SendQueueInterface::DataToSend(
1, RTC::SCTP::UserData(1, 0, 101, 0, 42, std::vector<uint8_t>(4), true, false, true));
});
producer1.PushBytesToSend(PayloadLength);
producer1.PushProduce(
[](uint64_t, size_t)
{
return RTC::SCTP::SendQueueInterface::DataToSend(
1, RTC::SCTP::UserData(1, 0, 101, 0, 42, std::vector<uint8_t>(4), false, false, true));
});
producer1.PushBytesToSend(PayloadLength);
producer1.PushProduce(
[](uint64_t, size_t)
{
return RTC::SCTP::SendQueueInterface::DataToSend(
1, RTC::SCTP::UserData(1, 0, 101, 0, 42, std::vector<uint8_t>(4), false, true, true));
});
producer1.PushBytesToSend(PayloadLength);
producer1.PushProduce(createChunk(2, 1, 102));
producer1.PushBytesToSend(0);
auto stream1 = scheduler.CreateStream(std::addressof(producer1), 1, 2);
stream1->MayMakeActive();
MockStreamProducer producer2;
producer2.PushBytesToSend(PayloadLength); producer2.PushProduce(createChunk(3, 2, 200));
producer2.PushBytesToSend(PayloadLength);
producer2.PushProduce(createChunk(4, 2, 201));
producer2.PushBytesToSend(PayloadLength);
producer2.PushProduce(createChunk(5, 2, 202));
producer2.PushBytesToSend(0);
auto stream2 = scheduler.CreateStream(std::addressof(producer2), 2, 2);
stream2->MayMakeActive();
REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 100));
REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 200));
REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 101));
REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 101));
REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 101));
REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 201));
REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 102));
REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 202));
REQUIRE(!scheduler.Produce(NowMs, Mtu).has_value());
}
SECTION("single stream can be resumed")
{
RTC::SCTP::StreamScheduler scheduler(Mtu);
MockStreamProducer producer1;
producer1.PushBytesToSend(PayloadLength);
producer1.PushProduce(createChunk(0, 1, 100));
producer1.PushBytesToSend(PayloadLength);
producer1.PushProduce(createChunk(1, 1, 101));
producer1.PushBytesToSend(PayloadLength);
producer1.PushProduce(createChunk(2, 1, 102));
producer1.PushBytesToSend(PayloadLength); producer1.PushBytesToSend(0);
auto stream1 = scheduler.CreateStream(std::addressof(producer1), 1, 2);
stream1->MayMakeActive();
REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 100));
REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 101));
stream1->MakeInactive();
REQUIRE(!scheduler.Produce(NowMs, Mtu).has_value());
stream1->MayMakeActive();
REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 102));
REQUIRE(!scheduler.Produce(NowMs, Mtu).has_value());
}
SECTION("will round-robin with paused stream")
{
RTC::SCTP::StreamScheduler scheduler(Mtu);
MockStreamProducer producer1;
producer1.PushBytesToSend(PayloadLength);
producer1.PushProduce(createChunk(0, 1, 100));
producer1.PushBytesToSend(PayloadLength);
producer1.PushProduce(createChunk(1, 1, 101));
producer1.PushBytesToSend(PayloadLength);
producer1.PushProduce(createChunk(2, 1, 102));
producer1.PushBytesToSend(PayloadLength); producer1.PushBytesToSend(0);
auto stream1 = scheduler.CreateStream(std::addressof(producer1), 1, 2);
stream1->MayMakeActive();
MockStreamProducer producer2;
producer2.PushBytesToSend(PayloadLength); producer2.PushProduce(createChunk(3, 2, 200));
producer2.PushBytesToSend(PayloadLength);
producer2.PushProduce(createChunk(4, 2, 201));
producer2.PushBytesToSend(PayloadLength);
producer2.PushProduce(createChunk(5, 2, 202));
producer2.PushBytesToSend(0);
auto stream2 = scheduler.CreateStream(std::addressof(producer2), 2, 2);
stream2->MayMakeActive();
REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 100));
REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 200));
stream1->MakeInactive();
REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 201));
REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 202));
stream1->MayMakeActive();
REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 101));
REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 102));
REQUIRE(!scheduler.Produce(NowMs, Mtu).has_value());
}
SECTION("will distribute round-robin packets evenly between two streams")
{
RTC::SCTP::StreamScheduler scheduler(Mtu);
const TestStream stream1(scheduler, 1, 1);
const TestStream stream2(scheduler, 2, 1);
const auto packetCounts = getPacketCounts(scheduler, 10);
REQUIRE(packetCounts.at(1) == 5);
REQUIRE(packetCounts.at(2) == 5);
}
SECTION("will distribute evenly with paused and added streams")
{
RTC::SCTP::StreamScheduler scheduler(Mtu);
const TestStream stream1(scheduler, 1, 1);
TestStream stream2(scheduler, 2, 1);
const auto counts1 = getPacketCounts(scheduler, 10);
REQUIRE(counts1.at(1) == 5);
REQUIRE(counts1.at(2) == 5);
stream2.GetStream().MakeInactive();
const TestStream stream3(scheduler, 3, 1);
const TestStream stream4(scheduler, 4, 1);
const auto counts2 = getPacketCounts(scheduler, 15);
REQUIRE(counts2.at(1) == 5);
REQUIRE(!counts2.contains(2));
REQUIRE(counts2.at(3) == 5);
REQUIRE(counts2.at(4) == 5);
stream2.GetStream().MayMakeActive();
const auto counts3 = getPacketCounts(scheduler, 20);
REQUIRE(counts3.at(1) == 5);
REQUIRE(counts3.at(2) == 5);
REQUIRE(counts3.at(3) == 5);
REQUIRE(counts3.at(4) == 5);
}
SECTION("will do fair queuing with same priority")
{
RTC::SCTP::StreamScheduler scheduler(Mtu);
scheduler.EnableMessageInterleaving(true);
constexpr size_t SmallPacket{ 30 };
constexpr size_t LargePacket{ 70 };
MockStreamProducer producer1;
producer1.PushBytesToSend(SmallPacket); producer1.PushProduce(createChunk(0, 1, 100, SmallPacket));
producer1.PushBytesToSend(SmallPacket);
producer1.PushProduce(createChunk(1, 1, 101, SmallPacket));
producer1.PushBytesToSend(SmallPacket);
producer1.PushProduce(createChunk(2, 1, 102, SmallPacket));
producer1.PushBytesToSend(0);
auto stream1 = scheduler.CreateStream(std::addressof(producer1), 1, 2);
stream1->MayMakeActive();
MockStreamProducer producer2;
producer2.PushBytesToSend(LargePacket); producer2.PushProduce(createChunk(3, 2, 200, LargePacket));
producer2.PushBytesToSend(LargePacket);
producer2.PushProduce(createChunk(4, 2, 201, LargePacket));
producer2.PushBytesToSend(LargePacket);
producer2.PushProduce(createChunk(5, 2, 202, LargePacket));
producer2.PushBytesToSend(0);
auto stream2 = scheduler.CreateStream(std::addressof(producer2), 2, 2);
stream2->MayMakeActive();
REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 100)); REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 101)); REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 200)); REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 102)); REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 201)); REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 202)); REQUIRE(!scheduler.Produce(NowMs, Mtu).has_value());
}
SECTION("will do weighted fair queuing with same size and different priority")
{
RTC::SCTP::StreamScheduler scheduler(Mtu);
scheduler.EnableMessageInterleaving(true);
MockStreamProducer producer1;
producer1.PushBytesToSend(PayloadLength); producer1.PushProduce(createChunk(0, 1, 100));
producer1.PushBytesToSend(PayloadLength);
producer1.PushProduce(createChunk(1, 1, 101));
producer1.PushBytesToSend(PayloadLength);
producer1.PushProduce(createChunk(2, 1, 102));
producer1.PushBytesToSend(0);
auto stream1 = scheduler.CreateStream(std::addressof(producer1), 1, 125);
stream1->MayMakeActive();
MockStreamProducer producer2;
producer2.PushBytesToSend(PayloadLength); producer2.PushProduce(createChunk(3, 2, 200));
producer2.PushBytesToSend(PayloadLength);
producer2.PushProduce(createChunk(4, 2, 201));
producer2.PushBytesToSend(PayloadLength);
producer2.PushProduce(createChunk(5, 2, 202));
producer2.PushBytesToSend(0);
auto stream2 = scheduler.CreateStream(std::addressof(producer2), 2, 200);
stream2->MayMakeActive();
MockStreamProducer producer3;
producer3.PushBytesToSend(PayloadLength); producer3.PushProduce(createChunk(6, 3, 300));
producer3.PushBytesToSend(PayloadLength);
producer3.PushProduce(createChunk(7, 3, 301));
producer3.PushBytesToSend(PayloadLength);
producer3.PushProduce(createChunk(8, 3, 302));
producer3.PushBytesToSend(0);
auto stream3 = scheduler.CreateStream(std::addressof(producer3), 3, 500);
stream3->MayMakeActive();
REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 300)); REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 301)); REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 200)); REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 302)); REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 100)); REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 201)); REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 202)); REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 101)); REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 102)); REQUIRE(!scheduler.Produce(NowMs, Mtu).has_value());
}
SECTION("will do weighted fair queuing with different size and priority")
{
RTC::SCTP::StreamScheduler scheduler(Mtu);
scheduler.EnableMessageInterleaving(true);
constexpr size_t SmallPacket{ 20 };
constexpr size_t MediumPacket{ 50 };
constexpr size_t LargePacket{ 70 };
MockStreamProducer producer1;
producer1.PushBytesToSend(MediumPacket); producer1.PushProduce(createChunk(0, 1, 100, MediumPacket));
producer1.PushBytesToSend(SmallPacket); producer1.PushProduce(createChunk(1, 1, 101, SmallPacket));
producer1.PushBytesToSend(LargePacket); producer1.PushProduce(createChunk(2, 1, 102, LargePacket));
producer1.PushBytesToSend(0);
auto stream1 = scheduler.CreateStream(std::addressof(producer1), 1, 125);
stream1->MayMakeActive();
MockStreamProducer producer2;
producer2.PushBytesToSend(MediumPacket); producer2.PushProduce(createChunk(3, 2, 200, MediumPacket));
producer2.PushBytesToSend(LargePacket); producer2.PushProduce(createChunk(4, 2, 201, LargePacket));
producer2.PushBytesToSend(SmallPacket); producer2.PushProduce(createChunk(5, 2, 202, SmallPacket));
producer2.PushBytesToSend(0);
auto stream2 = scheduler.CreateStream(std::addressof(producer2), 2, 200);
stream2->MayMakeActive();
MockStreamProducer producer3;
producer3.PushBytesToSend(SmallPacket); producer3.PushProduce(createChunk(6, 3, 300, SmallPacket));
producer3.PushBytesToSend(MediumPacket); producer3.PushProduce(createChunk(7, 3, 301, MediumPacket));
producer3.PushBytesToSend(LargePacket); producer3.PushProduce(createChunk(8, 3, 302, LargePacket));
producer3.PushBytesToSend(0);
auto stream3 = scheduler.CreateStream(std::addressof(producer3), 3, 500);
stream3->MayMakeActive();
REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 300)); REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 301)); REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 200)); REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 302)); REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 100)); REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 101)); REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 201)); REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 202)); REQUIRE(checkDataToSendHasMid(scheduler.Produce(NowMs, Mtu), 102)); REQUIRE(!scheduler.Produce(NowMs, Mtu).has_value());
}
SECTION("will distribute WFQ packets in two streams by priority")
{
RTC::SCTP::StreamScheduler scheduler(Mtu);
scheduler.EnableMessageInterleaving(true);
const TestStream stream1(scheduler, 1, 100);
const TestStream stream2(scheduler, 2, 200);
const auto packetCounts = getPacketCounts(scheduler, 15);
REQUIRE(packetCounts.at(1) == 5);
REQUIRE(packetCounts.at(2) == 10);
}
SECTION("will distribute WFQ packets in four streams by priority")
{
RTC::SCTP::StreamScheduler scheduler(Mtu);
scheduler.EnableMessageInterleaving(true);
const TestStream stream1(scheduler, 1, 100);
const TestStream stream2(scheduler, 2, 200);
const TestStream stream3(scheduler, 3, 300);
const TestStream stream4(scheduler, 4, 400);
const auto packetCounts = getPacketCounts(scheduler, 50);
REQUIRE(packetCounts.at(1) == 5);
REQUIRE(packetCounts.at(2) == 10);
REQUIRE(packetCounts.at(3) == 15);
REQUIRE(packetCounts.at(4) == 20);
}
SECTION("will distribute from two streams fairly")
{
RTC::SCTP::StreamScheduler scheduler(Mtu);
scheduler.EnableMessageInterleaving(true);
const TestStream stream1(scheduler, 1, 100, 8);
const TestStream stream2(scheduler, 2, 400, 4);
const auto packetCounts = getPacketCounts(scheduler, 90);
REQUIRE(packetCounts.at(1) == 10);
REQUIRE(packetCounts.at(2) == 80);
}
}