1
0
mirror of https://github.com/facebookincubator/mvfst.git synced 2025-08-09 20:42:44 +03:00
Files
mvfst/quic/flowcontrol/test/QuicFlowControlTest.cpp
Matt Joras 72e677df33 Send windowed stream limit updates
Summary:
Implement sending stream limit updates in a windowed fashion, so that as a peer exhausts its streams we will grant it additional credit. This is implemented by having the stream manager check if an update is needed on removing streams, and the api layer potentially sending an update after it initiates the check for closed streams.

This also makes some driveby changes to use `std::lower_bound` instead of `std::find` for the sorted collections in the stream manager.

Reviewed By: yangchi

Differential Revision: D16808229

fbshipit-source-id: f6e3460d43e4d165e362164be00c0cec27cf1e79
2019-09-18 11:33:03 -07:00

876 lines
32 KiB
C++

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
#include <quic/api/test/MockQuicStats.h>
#include <quic/client/state/ClientStateMachine.h>
#include <quic/common/test/TestUtils.h>
#include <quic/flowcontrol/QuicFlowController.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
using namespace folly;
using namespace testing;
namespace quic {
namespace test {
class QuicFlowControlTest : public Test {
public:
void SetUp() override {
transportInfoCb_ = std::make_unique<MockQuicStats>();
conn_.streamManager = std::make_unique<QuicStreamManager>(
conn_, conn_.nodeType, conn_.transportSettings);
conn_.infoCallback = transportInfoCb_.get();
}
std::unique_ptr<MockQuicStats> transportInfoCb_;
QuicConnectionStateBase conn_{QuicNodeType::Client};
};
TEST_F(QuicFlowControlTest, MaybeSendConnWindowUpdate) {
conn_.flowControlState.windowSize = 500;
conn_.flowControlState.advertisedMaxOffset = 400;
conn_.flowControlState.sumCurReadOffset = 100;
// Should not send window update
EXPECT_CALL(*transportInfoCb_, onConnFlowControlUpdate()).Times(0);
maybeSendConnWindowUpdate(conn_, Clock::now());
EXPECT_FALSE(conn_.pendingEvents.connWindowUpdate);
conn_.flowControlState.sumCurReadOffset += 200;
// Should send window update
EXPECT_CALL(*transportInfoCb_, onConnFlowControlUpdate()).Times(1);
maybeSendConnWindowUpdate(conn_, Clock::now());
EXPECT_TRUE(conn_.pendingEvents.connWindowUpdate);
}
TEST_F(QuicFlowControlTest, MaybeSendConnWindowUpdateTimeElapsed) {
conn_.flowControlState.windowSize = 500;
conn_.flowControlState.advertisedMaxOffset = 400;
conn_.flowControlState.sumCurReadOffset = 100;
conn_.lossState.srtt = 100us;
conn_.flowControlState.timeOfLastFlowControlUpdate = Clock::now();
// Should not send window update
EXPECT_CALL(*transportInfoCb_, onConnFlowControlUpdate()).Times(0);
// less than 2rtt passes
maybeSendConnWindowUpdate(
conn_, *conn_.flowControlState.timeOfLastFlowControlUpdate + 100us);
EXPECT_FALSE(conn_.pendingEvents.connWindowUpdate);
// Should send window update
EXPECT_CALL(*transportInfoCb_, onConnFlowControlUpdate()).Times(1);
maybeSendConnWindowUpdate(
conn_, *conn_.flowControlState.timeOfLastFlowControlUpdate + 300us);
EXPECT_TRUE(conn_.pendingEvents.connWindowUpdate);
}
TEST_F(QuicFlowControlTest, DontSendConnFlowControlTwice) {
conn_.flowControlState.windowSize = 500;
conn_.flowControlState.advertisedMaxOffset = 400;
conn_.flowControlState.sumCurReadOffset = 100;
conn_.lossState.srtt = 100us;
conn_.flowControlState.timeOfLastFlowControlUpdate = Clock::now();
// Should send window update
EXPECT_CALL(*transportInfoCb_, onConnFlowControlUpdate()).Times(1);
maybeSendConnWindowUpdate(
conn_, *conn_.flowControlState.timeOfLastFlowControlUpdate + 300us);
EXPECT_TRUE(conn_.pendingEvents.connWindowUpdate);
maybeSendConnWindowUpdate(
conn_, *conn_.flowControlState.timeOfLastFlowControlUpdate + 300us);
}
TEST_F(QuicFlowControlTest, NoStreamFlowControlUpdateOnTimeFlowUnchanged) {
conn_.flowControlState.windowSize = 500;
conn_.flowControlState.advertisedMaxOffset = 600;
conn_.flowControlState.sumCurReadOffset = 100;
conn_.lossState.srtt = 100us;
conn_.flowControlState.timeOfLastFlowControlUpdate = Clock::now();
EXPECT_CALL(*transportInfoCb_, onConnFlowControlUpdate()).Times(0);
maybeSendConnWindowUpdate(
conn_, *conn_.flowControlState.timeOfLastFlowControlUpdate + 300us);
EXPECT_FALSE(conn_.pendingEvents.connWindowUpdate);
}
TEST_F(QuicFlowControlTest, NoConnFlowControlUpdateOnTimeExpiredIfNotChanged) {
conn_.flowControlState.windowSize = 500;
conn_.flowControlState.advertisedMaxOffset = 600;
conn_.flowControlState.sumCurReadOffset = 100;
conn_.lossState.srtt = 100us;
conn_.flowControlState.timeOfLastFlowControlUpdate = Clock::now();
EXPECT_CALL(*transportInfoCb_, onConnFlowControlUpdate()).Times(0);
maybeSendConnWindowUpdate(
conn_, *conn_.flowControlState.timeOfLastFlowControlUpdate + 300us);
EXPECT_FALSE(conn_.pendingEvents.connWindowUpdate);
}
TEST_F(QuicFlowControlTest, MaybeSendConnWindowUpdateEnqueuesPendingEvent) {
conn_.flowControlState.windowSize = 100;
conn_.flowControlState.advertisedMaxOffset = 400;
conn_.flowControlState.sumCurReadOffset = 301;
EXPECT_CALL(*transportInfoCb_, onConnFlowControlUpdate()).Times(0);
maybeSendConnWindowUpdate(conn_, Clock::now());
EXPECT_FALSE(conn_.pendingEvents.connWindowUpdate);
conn_.flowControlState.windowSize = 500;
EXPECT_CALL(*transportInfoCb_, onConnFlowControlUpdate()).Times(1);
maybeSendConnWindowUpdate(conn_, Clock::now());
EXPECT_TRUE(conn_.pendingEvents.connWindowUpdate);
}
TEST_F(QuicFlowControlTest, GenerateMaxDataFrameChangeWindowSmaller) {
conn_.flowControlState.windowSize = 500;
conn_.flowControlState.advertisedMaxOffset = 400;
conn_.flowControlState.sumCurReadOffset = 300;
auto frame = generateMaxDataFrame(conn_);
EXPECT_EQ(800, frame.maximumData);
conn_.flowControlState.windowSize = 10;
// change the read bytes to be within the maybeSendUpdate size of the previous
// window
conn_.flowControlState.sumCurReadOffset =
conn_.flowControlState.advertisedMaxOffset - 15;
// put within current window
conn_.flowControlState.sumCurReadOffset =
conn_.flowControlState.advertisedMaxOffset - 4;
auto frame2 = generateMaxDataFrame(conn_);
EXPECT_EQ(frame2.maximumData, conn_.flowControlState.sumCurReadOffset + 10);
}
TEST_F(QuicFlowControlTest, GenerateMaxDataFrameChangeWindowLarger) {
conn_.flowControlState.windowSize = 500;
conn_.flowControlState.advertisedMaxOffset = 400;
conn_.flowControlState.sumCurReadOffset = 301;
auto frame = generateMaxDataFrame(conn_);
EXPECT_EQ(801, frame.maximumData);
onConnWindowUpdateSent(
conn_,
conn_.ackStates.appDataAckState.nextPacketNum,
frame.maximumData,
Clock::now());
EXPECT_EQ(801, conn_.flowControlState.advertisedMaxOffset);
conn_.flowControlState.windowSize = 1001;
auto frame2 = generateMaxDataFrame(conn_);
EXPECT_EQ(1302, frame2.maximumData);
onConnWindowUpdateSent(
conn_,
conn_.ackStates.appDataAckState.nextPacketNum,
frame2.maximumData,
Clock::now());
EXPECT_EQ(
frame2.maximumData,
conn_.flowControlState.sumCurReadOffset +
conn_.flowControlState.windowSize);
}
TEST_F(QuicFlowControlTest, MaybeSendStreamWindowUpdate) {
StreamId id = 3;
QuicStreamState stream(id, conn_);
stream.currentReadOffset = 100;
stream.flowControlState.windowSize = 500;
stream.flowControlState.advertisedMaxOffset = 400;
// Should not send window update
EXPECT_CALL(*transportInfoCb_, onStreamFlowControlUpdate()).Times(0);
maybeSendStreamWindowUpdate(stream, Clock::now());
EXPECT_FALSE(conn_.streamManager->pendingWindowUpdate(stream.id));
EXPECT_CALL(*transportInfoCb_, onStreamFlowControlUpdate()).Times(0);
maybeSendStreamWindowUpdate(stream, Clock::now());
stream.currentReadOffset += 200;
// Should send window update
EXPECT_CALL(*transportInfoCb_, onStreamFlowControlUpdate()).Times(1);
maybeSendStreamWindowUpdate(stream, Clock::now());
EXPECT_TRUE(conn_.streamManager->pendingWindowUpdate(stream.id));
}
TEST_F(QuicFlowControlTest, MaybeSendStreamWindowUpdateTimeElapsed) {
StreamId id = 3;
QuicStreamState stream(id, conn_);
stream.currentReadOffset = 100;
stream.flowControlState.windowSize = 500;
stream.flowControlState.advertisedMaxOffset = 400;
conn_.lossState.srtt = 100us;
stream.flowControlState.timeOfLastFlowControlUpdate = Clock::now();
// Should not send window update
EXPECT_CALL(*transportInfoCb_, onStreamFlowControlUpdate()).Times(0);
maybeSendStreamWindowUpdate(
stream, *stream.flowControlState.timeOfLastFlowControlUpdate + 100us);
EXPECT_FALSE(conn_.streamManager->pendingWindowUpdate(stream.id));
EXPECT_CALL(*transportInfoCb_, onStreamFlowControlUpdate()).Times(1);
maybeSendStreamWindowUpdate(
stream, *stream.flowControlState.timeOfLastFlowControlUpdate + 300us);
EXPECT_TRUE(conn_.streamManager->pendingWindowUpdate(stream.id));
}
TEST_F(QuicFlowControlTest, DontSendStreamWindowUpdateTwice) {
StreamId id = 3;
QuicStreamState stream(id, conn_);
stream.currentReadOffset = 100;
stream.flowControlState.windowSize = 500;
stream.flowControlState.advertisedMaxOffset = 400;
conn_.lossState.srtt = 100us;
stream.flowControlState.timeOfLastFlowControlUpdate = Clock::now();
EXPECT_CALL(*transportInfoCb_, onStreamFlowControlUpdate()).Times(1);
maybeSendStreamWindowUpdate(
stream, *stream.flowControlState.timeOfLastFlowControlUpdate + 300us);
EXPECT_TRUE(conn_.streamManager->pendingWindowUpdate(stream.id));
maybeSendStreamWindowUpdate(
stream, *stream.flowControlState.timeOfLastFlowControlUpdate + 300us);
}
TEST_F(QuicFlowControlTest, DontSendStreamWindowUpdateOnRemoteHalfClosed) {
StreamId id = 3;
QuicStreamState stream(id, conn_);
stream.send.state = StreamSendStates::Open();
stream.recv.state = StreamReceiveStates::Closed();
// Should not send window update
maybeSendStreamWindowUpdate(stream, Clock::now());
EXPECT_FALSE(conn_.streamManager->pendingWindowUpdate(stream.id));
}
TEST_F(QuicFlowControlTest, MaybeSendStreamWindowUpdateChangeWindowSmaller) {
StreamId id = 3;
QuicStreamState stream(id, conn_);
stream.currentReadOffset = 300;
stream.flowControlState.windowSize = 500;
stream.flowControlState.advertisedMaxOffset = 400;
// Should not send window update
EXPECT_CALL(*transportInfoCb_, onStreamFlowControlUpdate()).Times(1);
maybeSendStreamWindowUpdate(stream, Clock::now());
ASSERT_TRUE(conn_.streamManager->pendingWindowUpdate(stream.id));
auto sendTime = Clock::now();
onStreamWindowUpdateSent(
stream,
conn_.ackStates.appDataAckState.nextPacketNum,
generateMaxStreamDataFrame(stream).maximumData,
sendTime);
EXPECT_EQ(
conn_.ackStates.appDataAckState.nextPacketNum,
*stream.latestMaxStreamDataPacket);
stream.flowControlState.windowSize = 10;
// change the read bytes to be within the maybeSendUpdate size of the previous
// window
stream.currentReadOffset = stream.flowControlState.advertisedMaxOffset - 15;
// Should send window update
EXPECT_CALL(*transportInfoCb_, onStreamFlowControlUpdate()).Times(1);
maybeSendStreamWindowUpdate(stream, sendTime);
EXPECT_FALSE(conn_.streamManager->pendingWindowUpdate(stream.id));
// put within current window
stream.currentReadOffset = stream.flowControlState.advertisedMaxOffset - 4;
maybeSendStreamWindowUpdate(stream, sendTime);
EXPECT_EQ(
generateMaxStreamDataFrame(stream).maximumData,
stream.currentReadOffset + 10);
}
TEST_F(QuicFlowControlTest, MaybeWriteBlockedAfterAPIWrite) {
StreamId id = 3;
QuicStreamState stream(id, conn_);
stream.currentWriteOffset = 200;
stream.flowControlState.peerAdvertisedMaxOffset = 400;
EXPECT_CALL(*transportInfoCb_, onStreamFlowControlBlocked()).Times(0);
maybeWriteBlockAfterAPIWrite(stream);
EXPECT_FALSE(conn_.streamManager->hasBlocked());
stream.currentWriteOffset = 400;
stream.writeBuffer.append(IOBuf::copyBuffer("1234"));
EXPECT_CALL(*transportInfoCb_, onStreamFlowControlBlocked()).Times(0);
maybeWriteBlockAfterAPIWrite(stream);
EXPECT_FALSE(conn_.streamManager->hasBlocked());
stream.writeBuffer.clear();
stream.currentWriteOffset = 600;
stream.flowControlState.peerAdvertisedMaxOffset = 600;
EXPECT_CALL(*transportInfoCb_, onStreamFlowControlBlocked()).Times(1);
maybeWriteBlockAfterAPIWrite(stream);
EXPECT_TRUE(conn_.streamManager->hasBlocked());
}
TEST_F(QuicFlowControlTest, MaybeWriteBlockedAfterSocketWrite) {
StreamId id = 3;
QuicStreamState stream(id, conn_);
stream.currentWriteOffset = 200;
stream.flowControlState.peerAdvertisedMaxOffset = 400;
maybeWriteBlockAfterSocketWrite(stream);
EXPECT_FALSE(conn_.streamManager->hasBlocked());
// Don't add a blocked if there is nothing to write even the stream is
// flow control limited.
stream.currentWriteOffset = 400;
maybeWriteBlockAfterSocketWrite(stream);
EXPECT_CALL(*transportInfoCb_, onStreamFlowControlBlocked()).Times(0);
EXPECT_FALSE(conn_.streamManager->hasBlocked());
// Now write something
stream.writeBuffer.append(IOBuf::copyBuffer("1234"));
EXPECT_CALL(*transportInfoCb_, onStreamFlowControlBlocked()).Times(1);
maybeWriteBlockAfterSocketWrite(stream);
EXPECT_TRUE(conn_.streamManager->hasBlocked());
}
TEST_F(QuicFlowControlTest, MaybeSendStreamWindowUpdateChangeWindowLarger) {
StreamId id = 3;
QuicStreamState stream(id, conn_);
stream.currentReadOffset = 301;
stream.flowControlState.windowSize = 500;
stream.flowControlState.advertisedMaxOffset = 400;
// Should not send window update
EXPECT_CALL(*transportInfoCb_, onStreamFlowControlUpdate()).Times(1);
maybeSendStreamWindowUpdate(stream, Clock::now());
EXPECT_TRUE(conn_.streamManager->pendingWindowUpdate(stream.id));
onStreamWindowUpdateSent(
stream,
conn_.ackStates.appDataAckState.nextPacketNum,
generateMaxStreamDataFrame(stream).maximumData,
Clock::now());
EXPECT_EQ(
conn_.ackStates.appDataAckState.nextPacketNum,
*stream.latestMaxStreamDataPacket);
EXPECT_FALSE(conn_.streamManager->pendingWindowUpdate(stream.id));
stream.flowControlState.windowSize = 1001;
// Should send window update
EXPECT_CALL(*transportInfoCb_, onStreamFlowControlUpdate()).Times(1);
maybeSendStreamWindowUpdate(stream, Clock::now());
EXPECT_EQ(
generateMaxStreamDataFrame(stream).maximumData,
stream.currentReadOffset + stream.flowControlState.windowSize);
}
TEST_F(QuicFlowControlTest, SendingConnectionWindowUpdate) {
conn_.flowControlState.windowSize = 500;
conn_.flowControlState.advertisedMaxOffset = 400;
conn_.flowControlState.sumCurReadOffset = 300;
// Should send window update
EXPECT_CALL(*transportInfoCb_, onConnFlowControlUpdate()).Times(1);
maybeSendConnWindowUpdate(conn_, Clock::now());
EXPECT_TRUE(conn_.pendingEvents.connWindowUpdate);
auto frameOffset = generateMaxDataFrame(conn_).maximumData;
EXPECT_EQ(800, frameOffset);
// Clear out the window update.
auto sendTime = Clock::now();
onConnWindowUpdateSent(
conn_,
conn_.ackStates.appDataAckState.nextPacketNum,
frameOffset,
sendTime);
EXPECT_EQ(
conn_.ackStates.appDataAckState.nextPacketNum,
*conn_.latestMaxDataPacket);
EXPECT_FALSE(conn_.pendingEvents.connWindowUpdate);
EXPECT_EQ(conn_.flowControlState.advertisedMaxOffset, frameOffset);
EXPECT_EQ(*conn_.flowControlState.timeOfLastFlowControlUpdate, sendTime);
}
TEST_F(QuicFlowControlTest, SendingStreamWindowUpdate) {
StreamId id = 3;
QuicStreamState stream(id, conn_);
stream.currentReadOffset = 300;
stream.flowControlState.windowSize = 500;
stream.flowControlState.advertisedMaxOffset = 400;
// Should send window update
EXPECT_CALL(*transportInfoCb_, onStreamFlowControlUpdate()).Times(1);
maybeSendStreamWindowUpdate(stream, Clock::now());
EXPECT_TRUE(conn_.streamManager->pendingWindowUpdate(stream.id));
auto frameOffset = generateMaxStreamDataFrame(stream).maximumData;
EXPECT_EQ(800, frameOffset);
auto sendTime = Clock::now();
onStreamWindowUpdateSent(
stream,
conn_.ackStates.appDataAckState.nextPacketNum,
frameOffset,
sendTime);
EXPECT_EQ(
conn_.ackStates.appDataAckState.nextPacketNum,
*stream.latestMaxStreamDataPacket);
EXPECT_FALSE(conn_.streamManager->pendingWindowUpdate(stream.id));
EXPECT_EQ(stream.flowControlState.advertisedMaxOffset, frameOffset);
EXPECT_EQ(*stream.flowControlState.timeOfLastFlowControlUpdate, sendTime);
}
TEST_F(QuicFlowControlTest, LostConnectionWindowUpdate) {
conn_.flowControlState.windowSize = 500;
conn_.flowControlState.advertisedMaxOffset = 400;
conn_.flowControlState.sumCurReadOffset = 300;
onConnWindowUpdateLost(conn_);
EXPECT_TRUE(conn_.pendingEvents.connWindowUpdate);
EXPECT_EQ(generateMaxDataFrame(conn_).maximumData, 500 + 300);
}
TEST_F(QuicFlowControlTest, LostConnectionWindowUpdateSmallerWindow) {
conn_.flowControlState.windowSize = 10;
conn_.flowControlState.advertisedMaxOffset = 400;
conn_.flowControlState.sumCurReadOffset = 300;
onConnWindowUpdateLost(conn_);
ASSERT_TRUE(conn_.pendingEvents.connWindowUpdate);
EXPECT_EQ(generateMaxDataFrame(conn_).maximumData, 400);
}
TEST_F(QuicFlowControlTest, LostStreamWindowUpdate) {
StreamId id = 3;
QuicStreamState stream(id, conn_);
stream.currentReadOffset = 300;
stream.flowControlState.windowSize = 500;
stream.flowControlState.advertisedMaxOffset = 400;
// Should send window update
onStreamWindowUpdateLost(stream);
EXPECT_EQ(generateMaxStreamDataFrame(stream).maximumData, 500 + 300);
}
TEST_F(QuicFlowControlTest, LostStreamWindowUpdateHalfClosedRemote) {
StreamId id = 3;
QuicStreamState stream(id, conn_);
stream.currentReadOffset = 300;
stream.flowControlState.windowSize = 500;
stream.flowControlState.advertisedMaxOffset = 400;
stream.send.state = StreamSendStates::Open();
stream.recv.state = StreamReceiveStates::Closed();
// Should not send window update
onStreamWindowUpdateLost(stream);
EXPECT_FALSE(conn_.streamManager->pendingWindowUpdate(stream.id));
}
TEST_F(QuicFlowControlTest, LostStreamWindowUpdateSmallerWindow) {
StreamId id = 3;
QuicStreamState stream(id, conn_);
stream.currentReadOffset = 300;
stream.flowControlState.windowSize = 10;
stream.flowControlState.advertisedMaxOffset = 400;
// Should send window update
onStreamWindowUpdateLost(stream);
EXPECT_EQ(generateMaxStreamDataFrame(stream).maximumData, 400);
}
TEST_F(QuicFlowControlTest, HandleConnBlocked) {
conn_.flowControlState.windowSize = 500;
conn_.flowControlState.advertisedMaxOffset = 400;
conn_.flowControlState.sumCurReadOffset = 300;
handleConnBlocked(conn_);
EXPECT_TRUE(conn_.pendingEvents.connWindowUpdate);
EXPECT_EQ(generateMaxDataFrame(conn_).maximumData, 500 + 300);
}
TEST_F(QuicFlowControlTest, HandleConnBlockedSmallerWindow) {
conn_.flowControlState.windowSize = 10;
conn_.flowControlState.advertisedMaxOffset = 400;
conn_.flowControlState.sumCurReadOffset = 300;
handleConnBlocked(conn_);
EXPECT_TRUE(conn_.pendingEvents.connWindowUpdate);
EXPECT_EQ(generateMaxDataFrame(conn_).maximumData, 400);
}
TEST_F(QuicFlowControlTest, HandleStreamBlocked) {
StreamId id = 3;
QuicStreamState stream(id, conn_);
stream.currentReadOffset = 300;
stream.flowControlState.windowSize = 500;
stream.flowControlState.advertisedMaxOffset = 400;
handleStreamBlocked(stream);
EXPECT_EQ(generateMaxStreamDataFrame(stream).maximumData, 500 + 300);
}
TEST_F(QuicFlowControlTest, HandleStreamBlockedSmallerWindow) {
StreamId id = 3;
QuicStreamState stream(id, conn_);
stream.currentReadOffset = 300;
stream.flowControlState.windowSize = 10;
stream.flowControlState.advertisedMaxOffset = 400;
handleStreamBlocked(stream);
EXPECT_EQ(generateMaxStreamDataFrame(stream).maximumData, 400);
}
TEST_F(QuicFlowControlTest, UpdateFlowControlOnStreamData) {
conn_.flowControlState.sumMaxObservedOffset = 550;
conn_.flowControlState.sumCurReadOffset = 200;
conn_.flowControlState.windowSize = 400;
conn_.flowControlState.advertisedMaxOffset = 600;
StreamId id = 3;
QuicStreamState stream(id, conn_);
stream.currentReadOffset = 150;
stream.maxOffsetObserved = 200;
stream.flowControlState.windowSize = 100;
stream.flowControlState.advertisedMaxOffset = 250;
auto data1 = buildRandomInputData(10);
uint64_t buffer1EndOffset = 200 + data1->computeChainDataLength();
updateFlowControlOnStreamData(
stream, stream.maxOffsetObserved, buffer1EndOffset);
EXPECT_EQ(conn_.flowControlState.sumMaxObservedOffset, 560);
}
TEST_F(QuicFlowControlTest, UpdateFlowControlOnStreamDataUnchangedOffset) {
conn_.flowControlState.sumMaxObservedOffset = 550;
conn_.flowControlState.sumCurReadOffset = 200;
conn_.flowControlState.windowSize = 400;
conn_.flowControlState.advertisedMaxOffset = 600;
StreamId id = 3;
QuicStreamState stream(id, conn_);
stream.currentReadOffset = 150;
stream.maxOffsetObserved = 200;
stream.flowControlState.windowSize = 100;
stream.flowControlState.advertisedMaxOffset = 250;
uint64_t buffer1EndOffset = 100;
updateFlowControlOnStreamData(
stream, stream.maxOffsetObserved, buffer1EndOffset);
EXPECT_EQ(stream.maxOffsetObserved, 200);
EXPECT_EQ(conn_.flowControlState.sumMaxObservedOffset, 550);
}
TEST_F(QuicFlowControlTest, UpdateBadFlowControlOnStreamData) {
conn_.flowControlState.sumMaxObservedOffset = 550;
conn_.flowControlState.sumCurReadOffset = 200;
conn_.flowControlState.windowSize = 400;
conn_.flowControlState.advertisedMaxOffset = 600;
StreamId id = 3;
QuicStreamState stream(id, conn_);
stream.currentReadOffset = 150;
stream.maxOffsetObserved = 200;
stream.flowControlState.windowSize = 100;
stream.flowControlState.advertisedMaxOffset = 250;
auto data1 = buildRandomInputData(100);
uint64_t buffer1EndOffset = 200 + data1->computeChainDataLength();
// Stream flow control violation
EXPECT_THROW(
updateFlowControlOnStreamData(
stream, stream.maxOffsetObserved, buffer1EndOffset),
QuicTransportException);
stream.currentReadOffset = 200;
// Connection flow control violation
EXPECT_THROW(
updateFlowControlOnStreamData(
stream, stream.maxOffsetObserved, buffer1EndOffset),
QuicTransportException);
auto data2 = buildRandomInputData(50);
uint64_t buffer2EndOffset = 200 + data2->computeChainDataLength();
EXPECT_NO_THROW(updateFlowControlOnStreamData(
stream, stream.maxOffsetObserved, buffer2EndOffset));
EXPECT_EQ(conn_.flowControlState.sumMaxObservedOffset, 600);
}
TEST_F(QuicFlowControlTest, UpdateFlowControlOnRead) {
auto qLogger = std::make_shared<FileQLogger>();
conn_.qLogger = qLogger;
StreamId id = 3;
QuicStreamState stream(id, conn_);
stream.currentReadOffset = 200;
stream.flowControlState.windowSize = 200;
stream.flowControlState.advertisedMaxOffset = 250;
conn_.flowControlState.windowSize = 500;
conn_.flowControlState.advertisedMaxOffset = 400;
conn_.flowControlState.sumCurReadOffset = 100;
EXPECT_CALL(*transportInfoCb_, onConnFlowControlUpdate());
EXPECT_CALL(*transportInfoCb_, onStreamFlowControlUpdate());
updateFlowControlOnRead(stream, 100, Clock::now());
EXPECT_EQ(conn_.flowControlState.sumCurReadOffset, 200);
EXPECT_TRUE(conn_.streamManager->pendingWindowUpdate(stream.id));
EXPECT_EQ(generateMaxStreamDataFrame(stream).maximumData, 400);
std::vector<int> indices =
getQLogEventIndices(QLogEventType::TransportStateUpdate, qLogger);
EXPECT_EQ(indices.size(), 1);
auto tmp = std::move(qLogger->logs[indices[0]]);
auto event = dynamic_cast<QLogTransportStateUpdateEvent*>(tmp.get());
EXPECT_EQ(event->update, getFlowControlEvent(700));
}
TEST_F(QuicFlowControlTest, UpdateFlowControlOnWrite) {
StreamId id = 3;
QuicStreamState stream(id, conn_);
stream.currentWriteOffset = 200;
stream.flowControlState.peerAdvertisedMaxOffset = 300;
conn_.flowControlState.sumCurWriteOffset = 200;
EXPECT_CALL(*transportInfoCb_, onConnFlowControlBlocked()).Times(0);
updateFlowControlOnWriteToStream(stream, 100);
EXPECT_EQ(conn_.flowControlState.sumCurStreamBufferLen, 100);
EXPECT_CALL(*transportInfoCb_, onConnFlowControlBlocked()).Times(0);
updateFlowControlOnWriteToSocket(stream, 100);
EXPECT_EQ(conn_.flowControlState.sumCurWriteOffset, 300);
EXPECT_EQ(conn_.flowControlState.sumCurStreamBufferLen, 0);
EXPECT_FALSE(conn_.streamManager->flowControlUpdatedContains(id));
stream.currentWriteOffset = 300;
EXPECT_CALL(*transportInfoCb_, onConnFlowControlBlocked()).Times(0);
updateFlowControlOnWriteToStream(stream, 100);
EXPECT_EQ(conn_.flowControlState.sumCurStreamBufferLen, 100);
EXPECT_CALL(*transportInfoCb_, onConnFlowControlBlocked()).Times(0);
updateFlowControlOnWriteToSocket(stream, 100);
EXPECT_EQ(conn_.flowControlState.sumCurStreamBufferLen, 0);
EXPECT_EQ(conn_.flowControlState.sumCurWriteOffset, 400);
EXPECT_FALSE(conn_.streamManager->flowControlUpdatedContains(id));
conn_.flowControlState.peerAdvertisedMaxOffset = 500;
conn_.flowControlState.sumCurStreamBufferLen = 100;
stream.flowControlState.peerAdvertisedMaxOffset = 600;
EXPECT_CALL(*transportInfoCb_, onConnFlowControlBlocked()).Times(1);
updateFlowControlOnWriteToSocket(stream, 100);
}
TEST_F(QuicFlowControlTest, UpdateFlowControlOnWriteToStream) {
StreamId id = 3;
QuicStreamState stream(id, conn_);
stream.currentWriteOffset = 200;
conn_.flowControlState.sumCurStreamBufferLen = 100;
stream.flowControlState.peerAdvertisedMaxOffset = 300;
updateFlowControlOnWriteToStream(stream, 100);
EXPECT_EQ(conn_.flowControlState.sumCurStreamBufferLen, 200);
updateFlowControlOnWriteToSocket(stream, 150);
EXPECT_EQ(conn_.flowControlState.sumCurStreamBufferLen, 50);
updateFlowControlOnWriteToStream(stream, 100);
EXPECT_EQ(conn_.flowControlState.sumCurStreamBufferLen, 150);
}
TEST_F(QuicFlowControlTest, HandleStreamWindowUpdate) {
StreamId id = 3;
QuicStreamState stream(id, conn_);
stream.flowControlState.peerAdvertisedMaxOffset = 200;
auto qLogger = std::make_shared<FileQLogger>();
conn_.qLogger = qLogger;
handleStreamWindowUpdate(stream, 300, 2);
EXPECT_EQ(stream.flowControlState.peerAdvertisedMaxOffset, 300);
ASSERT_TRUE(conn_.streamManager->flowControlUpdatedContains(stream.id));
conn_.streamManager->removeFlowControlUpdated(stream.id);
handleStreamWindowUpdate(stream, 200, 1);
EXPECT_EQ(stream.flowControlState.peerAdvertisedMaxOffset, 300);
ASSERT_FALSE(conn_.streamManager->flowControlUpdatedContains(stream.id));
EXPECT_NO_THROW(handleStreamWindowUpdate(stream, 200, 3));
EXPECT_EQ(stream.flowControlState.peerAdvertisedMaxOffset, 300);
std::vector<int> indices =
getQLogEventIndices(QLogEventType::TransportStateUpdate, qLogger);
EXPECT_EQ(indices.size(), 1);
auto tmp = std::move(qLogger->logs[indices[0]]);
auto event = dynamic_cast<QLogTransportStateUpdateEvent*>(tmp.get());
EXPECT_EQ(event->update, getRxStreamWU(id, 2, 300));
}
TEST_F(QuicFlowControlTest, HandleConnWindowUpdate) {
conn_.flowControlState.peerAdvertisedMaxOffset = 200;
MaxDataFrame update1(300);
handleConnWindowUpdate(conn_, update1, 2);
EXPECT_EQ(conn_.flowControlState.peerAdvertisedMaxOffset, 300);
MaxDataFrame update2(200);
handleConnWindowUpdate(conn_, update2, 1);
EXPECT_EQ(conn_.flowControlState.peerAdvertisedMaxOffset, 300);
MaxDataFrame update3(200);
EXPECT_NO_THROW(handleConnWindowUpdate(conn_, update3, 3));
EXPECT_EQ(conn_.flowControlState.peerAdvertisedMaxOffset, 300);
}
TEST_F(QuicFlowControlTest, WritableList) {
StreamId id = 3;
QuicStreamState stream(id, conn_);
stream.currentWriteOffset = 100;
stream.flowControlState.peerAdvertisedMaxOffset = 200;
conn_.streamManager->updateWritableStreams(stream);
EXPECT_FALSE(conn_.streamManager->writableContains(id));
auto buf = IOBuf::create(100);
buf->append(100);
writeDataToQuicStream(stream, std::move(buf), false);
conn_.streamManager->updateWritableStreams(stream);
EXPECT_TRUE(conn_.streamManager->writableContains(id));
// Flow control
stream.flowControlState.peerAdvertisedMaxOffset = stream.currentWriteOffset;
conn_.streamManager->updateWritableStreams(stream);
EXPECT_FALSE(conn_.streamManager->writableContains(id));
// Fin
writeDataToQuicStream(stream, nullptr, true);
stream.writeBuffer.clear();
stream.currentWriteOffset += 100;
stream.flowControlState.peerAdvertisedMaxOffset = stream.currentWriteOffset;
conn_.streamManager->updateWritableStreams(stream);
EXPECT_TRUE(conn_.streamManager->writableContains(id));
// After Fin
stream.currentWriteOffset++;
conn_.streamManager->updateWritableStreams(stream);
EXPECT_FALSE(conn_.streamManager->writableContains(id));
}
TEST_F(QuicFlowControlTest, GetSendStreamFlowControlBytes) {
StreamId id = 3;
QuicStreamState stream(id, conn_);
stream.flowControlState.peerAdvertisedMaxOffset = 300;
stream.currentWriteOffset = 200;
EXPECT_EQ(100, getSendStreamFlowControlBytesWire(stream));
}
TEST_F(QuicFlowControlTest, GetSendStreamFlowControlBytesAPIEmpty) {
StreamId id = 3;
QuicStreamState stream(id, conn_);
auto buf = IOBuf::create(200);
buf->append(200);
stream.writeBuffer.append(std::move(buf));
stream.flowControlState.peerAdvertisedMaxOffset = 300;
stream.currentWriteOffset = 200;
EXPECT_EQ(getSendStreamFlowControlBytesAPI(stream), 0);
}
TEST_F(QuicFlowControlTest, GetSendStreamFlowControlBytesAPIPartial) {
StreamId id = 3;
QuicStreamState stream(id, conn_);
auto buf = IOBuf::create(200);
buf->append(200);
stream.writeBuffer.append(std::move(buf));
stream.flowControlState.peerAdvertisedMaxOffset = 500;
stream.currentWriteOffset = 200;
EXPECT_EQ(getSendStreamFlowControlBytesAPI(stream), 100);
}
TEST_F(QuicFlowControlTest, GetSendConnFlowControlBytes) {
conn_.flowControlState.sumCurWriteOffset = 200;
conn_.flowControlState.peerAdvertisedMaxOffset = 200;
EXPECT_EQ(0, getSendConnFlowControlBytesWire(conn_));
conn_.flowControlState.sumCurWriteOffset = 100;
conn_.flowControlState.peerAdvertisedMaxOffset = 200;
EXPECT_EQ(100, getSendConnFlowControlBytesWire(conn_));
}
TEST_F(QuicFlowControlTest, GetSendConnFlowControlBytesAPI) {
StreamId id = 3;
QuicStreamState stream(id, conn_);
conn_.flowControlState.sumCurWriteOffset = 200;
conn_.flowControlState.peerAdvertisedMaxOffset = 400;
conn_.flowControlState.sumCurStreamBufferLen = 200;
EXPECT_EQ(getSendConnFlowControlBytesAPI(conn_), 0);
conn_.flowControlState.sumCurStreamBufferLen = 300;
EXPECT_EQ(getSendConnFlowControlBytesAPI(conn_), 0);
conn_.flowControlState.sumCurStreamBufferLen = 100;
EXPECT_EQ(getSendConnFlowControlBytesAPI(conn_), 100);
}
TEST_F(QuicFlowControlTest, GetRecvConnFlowControlBytes) {
conn_.flowControlState.sumCurReadOffset = 200;
conn_.flowControlState.advertisedMaxOffset = 300;
EXPECT_EQ(100, getRecvConnFlowControlBytes(conn_));
conn_.flowControlState.sumCurReadOffset = 200;
conn_.flowControlState.advertisedMaxOffset = 200;
EXPECT_EQ(0, getRecvConnFlowControlBytes(conn_));
}
TEST_F(QuicFlowControlTest, GetRecvStreamFlowControlBytes) {
StreamId id = 3;
QuicStreamState stream(id, conn_);
stream.currentReadOffset = 200;
stream.flowControlState.advertisedMaxOffset = 300;
EXPECT_EQ(100, getRecvStreamFlowControlBytes(stream));
stream.currentReadOffset = 200;
stream.flowControlState.advertisedMaxOffset = 200;
EXPECT_EQ(0, getRecvStreamFlowControlBytes(stream));
// Current read offset can be greater than advertised max offset, since
// that's how we account for whether or not we read the eof.
stream.currentReadOffset = 201;
stream.flowControlState.advertisedMaxOffset = 200;
EXPECT_EQ(0, getRecvStreamFlowControlBytes(stream));
}
TEST_F(QuicFlowControlTest, OnConnWindowUpdateSentWithoutPendingEvent) {
EXPECT_FALSE(conn_.pendingEvents.connWindowUpdate);
conn_.flowControlState.windowSize = 1000;
conn_.flowControlState.advertisedMaxOffset = 0;
conn_.flowControlState.sumCurReadOffset = 0;
onConnWindowUpdateSent(
conn_, conn_.ackStates.appDataAckState.nextPacketNum, 1000, Clock::now());
EXPECT_EQ(
conn_.ackStates.appDataAckState.nextPacketNum,
*conn_.latestMaxDataPacket);
EXPECT_EQ(1000, conn_.flowControlState.advertisedMaxOffset);
EXPECT_FALSE(conn_.pendingEvents.connWindowUpdate);
}
TEST_F(QuicFlowControlTest, OnStreamWindowUpdateSentWithoutPendingEvent) {
StreamId id = 4;
QuicStreamState stream(id, conn_);
stream.currentReadOffset = 0;
stream.flowControlState.advertisedMaxOffset = 0;
stream.flowControlState.windowSize = 1000;
onStreamWindowUpdateSent(
stream,
conn_.ackStates.appDataAckState.nextPacketNum,
1000,
Clock::now());
EXPECT_EQ(
conn_.ackStates.appDataAckState.nextPacketNum,
*stream.latestMaxStreamDataPacket);
EXPECT_EQ(1000, stream.flowControlState.advertisedMaxOffset);
EXPECT_FALSE(conn_.streamManager->pendingWindowUpdate(id));
}
} // namespace test
} // namespace quic