/* -*- 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 listener = new CapsuleParserListener(); UniquePtr parser = MakeUnique(listener); parser->ProcessCapsuleData(buffer.Elements(), buffer.Length()); nsTArray 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 listener = new CapsuleParserListener(); UniquePtr parser = MakeUnique(listener); parser->ProcessCapsuleData(buffer.Elements(), buffer.Length()); nsTArray 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); }