Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "TestCommon.h"
#include "gtest/gtest.h"
#include "WebTransportFlowControl.h"
#include "Capsule.h"
#include "CapsuleEncoder.h"
#include "CapsuleParser.h"
using namespace mozilla;
using namespace mozilla::net;
TEST(SenderFlowControlTest, BlockedAtZero)
{
SenderFlowControlBase fc(0);
fc.Blocked();
EXPECT_EQ(*fc.BlockedNeeded(), 0u);
}
TEST(SenderFlowControlTest, Blocked)
{
SenderFlowControlBase fc(10);
fc.Blocked();
EXPECT_EQ(*fc.BlockedNeeded(), 10u);
}
TEST(SenderFlowControlTest, UpdateConsume)
{
SenderFlowControlBase fc(10);
fc.Consume(10);
EXPECT_EQ(fc.Available(), 0u);
fc.Update(5);
EXPECT_EQ(fc.Available(), 0u);
fc.Update(15);
EXPECT_EQ(fc.Available(), 5u);
fc.Consume(3);
EXPECT_EQ(fc.Available(), 2u);
}
TEST(SenderFlowControlTest, UpdateClearsBlocked)
{
SenderFlowControlBase fc(10);
fc.Blocked();
EXPECT_EQ(*fc.BlockedNeeded(), 10u);
fc.Update(5);
EXPECT_EQ(*fc.BlockedNeeded(), 10u);
fc.Update(11);
EXPECT_EQ(fc.BlockedNeeded(), Nothing());
}
TEST(LocalStreamLimitsTest, StreamIdAllocation)
{
LocalStreamLimits fc;
fc[WebTransportStreamType::BiDi].Update(2);
fc[WebTransportStreamType::UniDi].Update(1);
// Add streams
EXPECT_EQ(*fc.TakeStreamId(WebTransportStreamType::BiDi), StreamId(0u));
EXPECT_EQ(*fc.TakeStreamId(WebTransportStreamType::BiDi), StreamId(4u));
EXPECT_TRUE(fc.TakeStreamId(WebTransportStreamType::BiDi).isNothing());
EXPECT_EQ(*fc.TakeStreamId(WebTransportStreamType::UniDi), StreamId(2u));
EXPECT_TRUE(fc.TakeStreamId(WebTransportStreamType::UniDi).isNothing());
auto encoder = fc[WebTransportStreamType::BiDi].CreateStreamsBlockedCapsule();
EXPECT_TRUE(encoder.isSome());
auto extractLimitFromEncoder = [](CapsuleEncoder& encoder) -> uint64_t {
auto buffer = encoder.GetBuffer();
RefPtr<CapsuleParserListener> listener = new CapsuleParserListener();
UniquePtr<CapsuleParser> parser = MakeUnique<CapsuleParser>(listener);
parser->ProcessCapsuleData(buffer.Elements(), buffer.Length());
nsTArray<Capsule> parsed = listener->GetParsedCapsules();
WebTransportStreamsBlockedCapsule blocked =
parsed[0].GetWebTransportStreamsBlockedCapsule();
return blocked.mLimit;
};
EXPECT_EQ(extractLimitFromEncoder(*encoder), 2u);
// Increase limit
fc[WebTransportStreamType::BiDi].Update(3);
fc[WebTransportStreamType::UniDi].Update(2);
EXPECT_EQ(*fc.TakeStreamId(WebTransportStreamType::BiDi), StreamId(8u));
EXPECT_TRUE(fc.TakeStreamId(WebTransportStreamType::BiDi).isNothing());
EXPECT_EQ(*fc.TakeStreamId(WebTransportStreamType::UniDi), StreamId(6u));
EXPECT_TRUE(fc.TakeStreamId(WebTransportStreamType::UniDi).isNothing());
auto encoder1 =
fc[WebTransportStreamType::UniDi].CreateStreamsBlockedCapsule();
EXPECT_TRUE(encoder1.isSome());
EXPECT_EQ(extractLimitFromEncoder(*encoder1), 2u);
auto encoder2 =
fc[WebTransportStreamType::BiDi].CreateStreamsBlockedCapsule();
EXPECT_TRUE(encoder2.isSome());
EXPECT_EQ(extractLimitFromEncoder(*encoder2), 3u);
}
TEST(ReceiverFlowControlTest, NoNeedMaxAllowedFrameAtStart)
{
ReceiverFlowControlBase fc(0);
EXPECT_FALSE(fc.CapsuleNeeded());
}
TEST(ReceiverFlowControlTest, MaxAllowedAfterItemsRetired)
{
ReceiverFlowControlBase fc(100);
fc.Retire(49);
EXPECT_FALSE(fc.CapsuleNeeded());
fc.Retire(51);
EXPECT_TRUE(fc.CapsuleNeeded());
EXPECT_EQ(fc.NextLimit(), 151u);
}
TEST(ReceiverFlowControlTest, ForceSendMaxAllowed)
{
ReceiverFlowControlBase fc(100);
fc.Retire(10);
EXPECT_FALSE(fc.CapsuleNeeded());
}
TEST(ReceiverFlowControlTest, MultipleRetriesAfterFramePendingIsSet)
{
ReceiverFlowControlBase fc(100);
fc.Retire(51);
EXPECT_TRUE(fc.CapsuleNeeded());
EXPECT_EQ(fc.NextLimit(), 151u);
fc.Retire(61);
EXPECT_TRUE(fc.CapsuleNeeded());
EXPECT_EQ(fc.NextLimit(), 161u);
fc.Retire(88);
EXPECT_TRUE(fc.CapsuleNeeded());
EXPECT_EQ(fc.NextLimit(), 188u);
fc.Retire(90);
EXPECT_TRUE(fc.CapsuleNeeded());
EXPECT_EQ(fc.NextLimit(), 190u);
fc.CapsuleSent(190);
EXPECT_FALSE(fc.CapsuleNeeded());
fc.Retire(141);
EXPECT_TRUE(fc.CapsuleNeeded());
EXPECT_EQ(fc.NextLimit(), 241u);
fc.CapsuleSent(241);
EXPECT_FALSE(fc.CapsuleNeeded());
}
TEST(ReceiverFlowControlTest, ChangingMaxActive)
{
ReceiverFlowControlBase fc(100);
fc.SetMaxActive(50);
EXPECT_FALSE(fc.CapsuleNeeded());
fc.Retire(60);
EXPECT_FALSE(fc.CapsuleNeeded());
fc.Retire(76);
EXPECT_TRUE(fc.CapsuleNeeded());
EXPECT_EQ(fc.NextLimit(), 126u);
fc.SetMaxActive(60);
EXPECT_TRUE(fc.CapsuleNeeded());
EXPECT_EQ(fc.NextLimit(), 136u);
fc.Retire(136);
EXPECT_TRUE(fc.CapsuleNeeded());
EXPECT_EQ(fc.NextLimit(), 196u);
}
TEST(RemoteStreamLimitsTest, HandlesStreamLimitLogicWithRawIds)
{
RemoteStreamLimits fc(/*bidi=*/2, /*unidi=*/1);
StreamId bidi0(1); // Stream 0 (BiDi, server-initiated)
StreamId bidi1(5); // Stream 1
StreamId bidi2(9); // Stream 2
StreamId bidi3(13); // Stream 3
StreamId uni0(3); // Stream 0 (UniDi, server-initiated)
StreamId uni1(7); // Stream 1
StreamId uni2(11); // Stream 2
// Initial streams should be allowed
EXPECT_TRUE(fc[WebTransportStreamType::BiDi].IsNewStream(bidi0).unwrap());
EXPECT_TRUE(fc[WebTransportStreamType::BiDi].IsNewStream(bidi1).unwrap());
EXPECT_TRUE(fc[WebTransportStreamType::UniDi].IsNewStream(uni0).unwrap());
// Exceed limits
EXPECT_EQ(fc[WebTransportStreamType::BiDi].IsNewStream(bidi2).unwrapErr(),
NS_ERROR_NOT_AVAILABLE);
EXPECT_EQ(fc[WebTransportStreamType::UniDi].IsNewStream(uni1).unwrapErr(),
NS_ERROR_NOT_AVAILABLE);
// Take stream IDs
EXPECT_EQ(fc[WebTransportStreamType::BiDi].TakeStreamId(), bidi0);
EXPECT_EQ(fc[WebTransportStreamType::BiDi].TakeStreamId(), bidi1);
EXPECT_EQ(fc[WebTransportStreamType::UniDi].TakeStreamId(), uni0);
// Retire and allow new BiDi stream
fc[WebTransportStreamType::BiDi].FlowControl().AddRetired(1);
fc[WebTransportStreamType::BiDi].FlowControl().SendFlowControlUpdate();
// Send MaxStreams capsule
auto encoder =
fc[WebTransportStreamType::BiDi].FlowControl().CreateMaxStreamsCapsule();
EXPECT_TRUE(encoder.isSome());
auto extractLimitFromEncoder = [](CapsuleEncoder& encoder) -> uint64_t {
auto buffer = encoder.GetBuffer();
RefPtr<CapsuleParserListener> listener = new CapsuleParserListener();
UniquePtr<CapsuleParser> parser = MakeUnique<CapsuleParser>(listener);
parser->ProcessCapsuleData(buffer.Elements(), buffer.Length());
nsTArray<Capsule> parsed = listener->GetParsedCapsules();
WebTransportMaxStreamsCapsule maxStreams =
parsed[0].GetWebTransportMaxStreamsCapsule();
return maxStreams.mLimit;
};
EXPECT_EQ(extractLimitFromEncoder(*encoder), 3u);
EXPECT_TRUE(fc[WebTransportStreamType::BiDi].IsNewStream(bidi2).unwrap());
EXPECT_EQ(fc[WebTransportStreamType::BiDi].TakeStreamId(), bidi2);
EXPECT_EQ(fc[WebTransportStreamType::BiDi].IsNewStream(bidi3).unwrapErr(),
NS_ERROR_NOT_AVAILABLE);
// Retire and allow new UniDi stream
fc[WebTransportStreamType::UniDi].FlowControl().AddRetired(1);
fc[WebTransportStreamType::UniDi].FlowControl().SendFlowControlUpdate();
auto encoder1 =
fc[WebTransportStreamType::UniDi].FlowControl().CreateMaxStreamsCapsule();
EXPECT_TRUE(encoder1.isSome());
EXPECT_EQ(extractLimitFromEncoder(*encoder1), 2u);
EXPECT_TRUE(fc[WebTransportStreamType::UniDi].IsNewStream(uni1).unwrap());
EXPECT_EQ(fc[WebTransportStreamType::UniDi].TakeStreamId(), uni1);
EXPECT_EQ(fc[WebTransportStreamType::UniDi].IsNewStream(uni2).unwrapErr(),
NS_ERROR_NOT_AVAILABLE);
}