1
0
mirror of https://github.com/facebookincubator/mvfst.git synced 2025-08-08 09:42:06 +03:00

Add CWND and writable bytes to PacketsWrittenEvent

Summary: Extend `PacketsWrittenEvent` to include the CWND and number of writable bytes from the congestion controller after the write completed. If no congestion controller is installed, these values are left blank.

Differential Revision: D32588533

fbshipit-source-id: 4b8361937fcab36daa17e5f6dc4386762987d2b4
This commit is contained in:
Brandon Schlinker
2022-03-28 23:26:30 -07:00
committed by Facebook GitHub Bot
parent f30df88637
commit da71a15307
7 changed files with 1038 additions and 24 deletions

View File

@@ -38,6 +38,18 @@ Observer::WriteEvent::Builder::setLastPacketSentTime(
return std::move(*this); return std::move(*this);
} }
Observer::WriteEvent::Builder&& Observer::WriteEvent::Builder::setCwndInBytes(
const folly::Optional<uint64_t>& maybeCwndInBytesIn) {
maybeCwndInBytes = maybeCwndInBytesIn;
return std::move(*this);
}
Observer::WriteEvent::Builder&& Observer::WriteEvent::Builder::setWritableBytes(
const folly::Optional<uint64_t>& maybeWritableBytesIn) {
maybeWritableBytes = maybeWritableBytesIn;
return std::move(*this);
}
Observer::WriteEvent Observer::WriteEvent::Builder::build() && { Observer::WriteEvent Observer::WriteEvent::Builder::build() && {
return WriteEvent(*this); return WriteEvent(*this);
} }
@@ -46,7 +58,9 @@ Observer::WriteEvent::WriteEvent(const WriteEvent::BuilderFields& builderFields)
: outstandingPackets(*CHECK_NOTNULL( : outstandingPackets(*CHECK_NOTNULL(
builderFields.maybeOutstandingPacketsRef.get_pointer())), builderFields.maybeOutstandingPacketsRef.get_pointer())),
writeCount(*CHECK_NOTNULL(builderFields.maybeWriteCount.get_pointer())), writeCount(*CHECK_NOTNULL(builderFields.maybeWriteCount.get_pointer())),
maybeLastPacketSentTime(builderFields.maybeLastPacketSentTime) {} maybeLastPacketSentTime(builderFields.maybeLastPacketSentTime),
maybeCwndInBytes(builderFields.maybeCwndInBytes),
maybeWritableBytes(builderFields.maybeWritableBytes) {}
Observer::AppLimitedEvent::Builder&& Observer::AppLimitedEvent::Builder&&
Observer::AppLimitedEvent::Builder::setOutstandingPackets( Observer::AppLimitedEvent::Builder::setOutstandingPackets(
@@ -75,6 +89,20 @@ Observer::AppLimitedEvent::Builder::setLastPacketSentTime(
return std::move(*this); return std::move(*this);
} }
Observer::AppLimitedEvent::Builder&&
Observer::AppLimitedEvent::Builder::setCwndInBytes(
const folly::Optional<uint64_t>& maybeCwndInBytesIn) {
maybeCwndInBytes = maybeCwndInBytesIn;
return std::move(*this);
}
Observer::AppLimitedEvent::Builder&&
Observer::AppLimitedEvent::Builder::setWritableBytes(
const folly::Optional<uint64_t>& maybeWritableBytesIn) {
maybeWritableBytes = maybeWritableBytesIn;
return std::move(*this);
}
Observer::AppLimitedEvent Observer::AppLimitedEvent::Builder::build() && { Observer::AppLimitedEvent Observer::AppLimitedEvent::Builder::build() && {
return AppLimitedEvent(std::move(*this)); return AppLimitedEvent(std::move(*this));
} }
@@ -111,6 +139,20 @@ Observer::PacketsWrittenEvent::Builder::setLastPacketSentTime(
return std::move(*this); return std::move(*this);
} }
Observer::PacketsWrittenEvent::Builder&&
Observer::PacketsWrittenEvent::Builder::setCwndInBytes(
const folly::Optional<uint64_t>& maybeCwndInBytesIn) {
maybeCwndInBytes = maybeCwndInBytesIn;
return std::move(*this);
}
Observer::PacketsWrittenEvent::Builder&&
Observer::PacketsWrittenEvent::Builder::setWritableBytes(
const folly::Optional<uint64_t>& maybeWritableBytesIn) {
maybeWritableBytes = maybeWritableBytesIn;
return std::move(*this);
}
Observer::PacketsWrittenEvent::Builder&& Observer::PacketsWrittenEvent::Builder&&
Observer::PacketsWrittenEvent::Builder::setNumPacketsWritten( Observer::PacketsWrittenEvent::Builder::setNumPacketsWritten(
const uint64_t numPacketsWrittenIn) { const uint64_t numPacketsWrittenIn) {

View File

@@ -111,12 +111,24 @@ class Observer {
// Timestamp when packet was last written // Timestamp when packet was last written
const folly::Optional<TimePoint> maybeLastPacketSentTime; const folly::Optional<TimePoint> maybeLastPacketSentTime;
// CWND in bytes.
//
// Optional to handle cases where congestion controller not used.
const folly::Optional<uint64_t> maybeCwndInBytes;
// Writable bytes.
//
// Optional to handle cases where congestion controller not used.
const folly::Optional<uint64_t> maybeWritableBytes;
struct BuilderFields { struct BuilderFields {
folly::Optional< folly::Optional<
std::reference_wrapper<const std::deque<OutstandingPacket>>> std::reference_wrapper<const std::deque<OutstandingPacket>>>
maybeOutstandingPacketsRef; maybeOutstandingPacketsRef;
folly::Optional<uint64_t> maybeWriteCount; folly::Optional<uint64_t> maybeWriteCount;
folly::Optional<TimePoint> maybeLastPacketSentTime; folly::Optional<TimePoint> maybeLastPacketSentTime;
folly::Optional<uint64_t> maybeCwndInBytes;
folly::Optional<uint64_t> maybeWritableBytes;
explicit BuilderFields() = default; explicit BuilderFields() = default;
}; };
@@ -127,6 +139,10 @@ class Observer {
Builder&& setLastPacketSentTime(const TimePoint& lastPacketSentTimeIn); Builder&& setLastPacketSentTime(const TimePoint& lastPacketSentTimeIn);
Builder&& setLastPacketSentTime( Builder&& setLastPacketSentTime(
const folly::Optional<TimePoint>& maybeLastPacketSentTimeIn); const folly::Optional<TimePoint>& maybeLastPacketSentTimeIn);
Builder&& setCwndInBytes(
const folly::Optional<uint64_t>& maybeCwndInBytesIn);
Builder&& setWritableBytes(
const folly::Optional<uint64_t>& maybeWritableBytesIn);
WriteEvent build() &&; WriteEvent build() &&;
explicit Builder() = default; explicit Builder() = default;
}; };
@@ -149,6 +165,10 @@ class Observer {
Builder&& setLastPacketSentTime(const TimePoint& lastPacketSentTimeIn); Builder&& setLastPacketSentTime(const TimePoint& lastPacketSentTimeIn);
Builder&& setLastPacketSentTime( Builder&& setLastPacketSentTime(
const folly::Optional<TimePoint>& maybeLastPacketSentTimeIn); const folly::Optional<TimePoint>& maybeLastPacketSentTimeIn);
Builder&& setCwndInBytes(
const folly::Optional<uint64_t>& maybeCwndInBytesIn);
Builder&& setWritableBytes(
const folly::Optional<uint64_t>& maybeWritableBytesIn);
AppLimitedEvent build() &&; AppLimitedEvent build() &&;
explicit Builder() = default; explicit Builder() = default;
}; };
@@ -186,6 +206,10 @@ class Observer {
Builder&& setNumAckElicitingPacketsWritten( Builder&& setNumAckElicitingPacketsWritten(
const uint64_t numAckElicitingPacketsWrittenIn); const uint64_t numAckElicitingPacketsWrittenIn);
Builder&& setNumBytesWritten(const uint64_t numBytesWrittenIn); Builder&& setNumBytesWritten(const uint64_t numBytesWrittenIn);
Builder&& setCwndInBytes(
const folly::Optional<uint64_t>& maybeCwndInBytesIn);
Builder&& setWritableBytes(
const folly::Optional<uint64_t>& maybeWritableBytesIn);
PacketsWrittenEvent build() &&; PacketsWrittenEvent build() &&;
explicit Builder() = default; explicit Builder() = default;
}; };

View File

@@ -3547,6 +3547,16 @@ void QuicTransportBase::notifyStartWritingFromAppRateLimited() {
.setOutstandingPackets(conn_->outstandings.packets) .setOutstandingPackets(conn_->outstandings.packets)
.setWriteCount(conn_->writeCount) .setWriteCount(conn_->writeCount)
.setLastPacketSentTime(conn_->lossState.maybeLastPacketSentTime) .setLastPacketSentTime(conn_->lossState.maybeLastPacketSentTime)
.setCwndInBytes(
conn_->congestionController
? folly::Optional<uint64_t>(
conn_->congestionController->getCongestionWindow())
: folly::none)
.setWritableBytes(
conn_->congestionController
? folly::Optional<uint64_t>(
conn_->congestionController->getWritableBytes())
: folly::none)
.build(); .build();
for (const auto& cb : *observers_) { for (const auto& cb : *observers_) {
if (cb->getConfig().appRateLimitedEvents) { if (cb->getConfig().appRateLimitedEvents) {
@@ -3564,6 +3574,16 @@ void QuicTransportBase::notifyPacketsWritten(
.setOutstandingPackets(conn_->outstandings.packets) .setOutstandingPackets(conn_->outstandings.packets)
.setWriteCount(conn_->writeCount) .setWriteCount(conn_->writeCount)
.setLastPacketSentTime(conn_->lossState.maybeLastPacketSentTime) .setLastPacketSentTime(conn_->lossState.maybeLastPacketSentTime)
.setCwndInBytes(
conn_->congestionController
? folly::Optional<uint64_t>(
conn_->congestionController->getCongestionWindow())
: folly::none)
.setWritableBytes(
conn_->congestionController
? folly::Optional<uint64_t>(
conn_->congestionController->getWritableBytes())
: folly::none)
.setNumPacketsWritten(numPacketsWritten) .setNumPacketsWritten(numPacketsWritten)
.setNumAckElicitingPacketsWritten(numAckElicitingPacketsWritten) .setNumAckElicitingPacketsWritten(numAckElicitingPacketsWritten)
.setNumBytesWritten(numBytesWritten) .setNumBytesWritten(numBytesWritten)
@@ -3581,6 +3601,16 @@ void QuicTransportBase::notifyAppRateLimited() {
.setOutstandingPackets(conn_->outstandings.packets) .setOutstandingPackets(conn_->outstandings.packets)
.setWriteCount(conn_->writeCount) .setWriteCount(conn_->writeCount)
.setLastPacketSentTime(conn_->lossState.maybeLastPacketSentTime) .setLastPacketSentTime(conn_->lossState.maybeLastPacketSentTime)
.setCwndInBytes(
conn_->congestionController
? folly::Optional<uint64_t>(
conn_->congestionController->getCongestionWindow())
: folly::none)
.setWritableBytes(
conn_->congestionController
? folly::Optional<uint64_t>(
conn_->congestionController->getWritableBytes())
: folly::none)
.build(); .build();
for (const auto& cb : *observers_) { for (const auto& cb : *observers_) {
if (cb->getConfig().appRateLimitedEvents) { if (cb->getConfig().appRateLimitedEvents) {

View File

@@ -19,6 +19,7 @@
#include <quic/common/BufUtil.h> #include <quic/common/BufUtil.h>
#include <quic/common/Timers.h> #include <quic/common/Timers.h>
#include <quic/common/test/TestUtils.h> #include <quic/common/test/TestUtils.h>
#include <quic/congestion_control/StaticCwndCongestionController.h>
#include <quic/dsr/Types.h> #include <quic/dsr/Types.h>
#include <quic/dsr/test/Mocks.h> #include <quic/dsr/test/Mocks.h>
#include <quic/handshake/test/Mocks.h> #include <quic/handshake/test/Mocks.h>
@@ -1060,6 +1061,266 @@ TEST_F(QuicTransportTest, ObserverPacketsWrittenCheckBytesSent) {
transport_ = nullptr; transport_ = nullptr;
} }
TEST_F(QuicTransportTest, ObserverWriteEventsCheckCwndPacketsWritable) {
InSequence s;
Observer::Config config = {};
config.packetsWrittenEvents = true;
config.appRateLimitedEvents = true;
auto cb1 = std::make_unique<StrictMock<MockObserver>>(config);
auto cb2 = std::make_unique<StrictMock<MockObserver>>(config);
auto cb3 = std::make_unique<StrictMock<MockObserver>>(Observer::Config());
const auto invokeForAllObservers =
[&cb1, &cb2, &cb3](const std::function<void(MockObserver&)>& fn) {
fn(*cb1);
fn(*cb2);
fn(*cb3);
};
const auto invokeForEachObserverWithTestEvents =
[&cb1, &cb2](const std::function<void(MockObserver&)>& fn) {
fn(*cb1);
fn(*cb2);
};
// install observers
invokeForAllObservers(([this](MockObserver& observer) {
EXPECT_CALL(observer, observerAttach(transport_.get()));
transport_->addObserver(&observer);
}));
EXPECT_THAT(
transport_->getObservers(),
UnorderedElementsAre(cb1.get(), cb2.get(), cb3.get()));
auto& conn = transport_->getConnectionState();
// install StaticCwndCongestionController
const auto cwndInBytes = 10000;
conn.congestionController = std::make_unique<StaticCwndCongestionController>(
StaticCwndCongestionController::CwndInBytes(cwndInBytes));
// update writeNum and upperBoundCurrentBytesWritable after each write/ACK
uint64_t writeNum = 1;
uint64_t upperBoundCurrentBytesWritable = cwndInBytes;
// write of 4000 stream bytes
{
const auto bytesToWrite = 4000;
// matcher for event from startWritingFromAppLimited
const auto startWritingFromAppLimitedMatcher = AllOf(
testing::Property(
&Observer::WriteEvent::getOutstandingPackets, testing::IsEmpty()),
testing::Field(
&Observer::WriteEvent::writeCount, testing::Eq(writeNum)),
testing::Field(
&Observer::WriteEvent::maybeCwndInBytes,
testing::Eq(folly::Optional<uint64_t>(cwndInBytes))),
testing::Field(
&Observer::WriteEvent::maybeWritableBytes,
testing::Eq(folly::Optional<uint64_t>(cwndInBytes))));
// matcher for event from packetsWritten
const auto packetsWrittenMatcher = AllOf(
testing::Property(
&Observer::WriteEvent::getOutstandingPackets, testing::SizeIs(4)),
testing::Field(
&Observer::WriteEvent::writeCount, testing::Eq(writeNum)),
testing::Field(
&Observer::WriteEvent::maybeCwndInBytes,
testing::Eq(folly::Optional<uint64_t>(cwndInBytes))),
testing::Field( // precise check below
&Observer::WriteEvent::maybeWritableBytes,
testing::Lt(folly::Optional<uint64_t>(
upperBoundCurrentBytesWritable - bytesToWrite))),
testing::Field(
&Observer::PacketsWrittenEvent::numPacketsWritten, testing::Eq(4)),
testing::Field(
&Observer::PacketsWrittenEvent::numAckElicitingPacketsWritten,
testing::Eq(4)),
testing::Field(
&Observer::PacketsWrittenEvent::numBytesWritten,
testing::Gt(bytesToWrite)));
// matcher for event from appRateLimited
const auto appRateLimitedMatcher = AllOf(
testing::Property(
&Observer::WriteEvent::getOutstandingPackets, testing::SizeIs(4)),
testing::Field(
&Observer::WriteEvent::writeCount, testing::Eq(writeNum)),
testing::Field(
&Observer::WriteEvent::maybeCwndInBytes,
testing::Eq(folly::Optional<uint64_t>(cwndInBytes))),
testing::Field( // precise check below
&Observer::WriteEvent::maybeWritableBytes,
testing::Lt(folly::Optional<uint64_t>(
upperBoundCurrentBytesWritable - bytesToWrite))));
invokeForEachObserverWithTestEvents(
([this, &startWritingFromAppLimitedMatcher](MockObserver& observer) {
EXPECT_CALL(
observer,
startWritingFromAppLimited(
transport_.get(), startWritingFromAppLimitedMatcher));
}));
invokeForEachObserverWithTestEvents(
([this,
&packetsWrittenMatcher,
cwndInBytes,
oldTInfo = transport_->getTransportInfo()](MockObserver& observer) {
EXPECT_CALL(
observer, packetsWritten(transport_.get(), packetsWrittenMatcher))
.WillOnce(([cwndInBytes, oldTInfo](
const auto& socket, const auto& event) {
EXPECT_EQ(
cwndInBytes - socket->getTransportInfo().bytesSent -
oldTInfo.bytesSent,
event.maybeWritableBytes);
}));
}));
invokeForEachObserverWithTestEvents(
([this,
&appRateLimitedMatcher,
cwndInBytes,
oldTInfo = transport_->getTransportInfo()](MockObserver& observer) {
EXPECT_CALL(
observer, appRateLimited(transport_.get(), appRateLimitedMatcher))
.WillOnce(([cwndInBytes, oldTInfo](
const auto& socket, const auto& event) {
EXPECT_EQ(
cwndInBytes - socket->getTransportInfo().bytesSent -
oldTInfo.bytesSent,
event.maybeWritableBytes);
}));
}));
auto stream = transport_->createBidirectionalStream().value();
transport_->writeChain(
stream, buildRandomInputData(bytesToWrite), false, nullptr);
transport_->updateWriteLooper(true);
loopForWrites();
loopForWrites();
// remove bytesToWrite from upperBoundCurrentBytesWritable
upperBoundCurrentBytesWritable -= bytesToWrite;
writeNum++;
}
// another write of 1000 stream bytes
{
const auto bytesToWrite = 1000;
// matcher for event from startWritingFromAppLimited
const auto startWritingFromAppLimitedMatcher = AllOf(
testing::Property(
&Observer::WriteEvent::getOutstandingPackets, testing::SizeIs(4)),
testing::Field(
&Observer::WriteEvent::writeCount, testing::Eq(writeNum)),
testing::Field(
&Observer::WriteEvent::maybeCwndInBytes,
testing::Eq(folly::Optional<uint64_t>(cwndInBytes))),
testing::Field(
&Observer::WriteEvent::maybeWritableBytes,
testing::Lt(
folly::Optional<uint64_t>(upperBoundCurrentBytesWritable))));
// matcher for event from packetsWritten
const auto packetsWrittenMatcher = AllOf(
testing::Property(
&Observer::WriteEvent::getOutstandingPackets, testing::SizeIs(5)),
testing::Field(
&Observer::WriteEvent::writeCount, testing::Eq(writeNum)),
testing::Field(
&Observer::WriteEvent::maybeCwndInBytes,
testing::Eq(folly::Optional<uint64_t>(cwndInBytes))),
testing::Field( // precise check below
&Observer::WriteEvent::maybeWritableBytes,
testing::Lt(folly::Optional<uint64_t>(
upperBoundCurrentBytesWritable - bytesToWrite))),
testing::Field(
&Observer::PacketsWrittenEvent::numPacketsWritten, testing::Eq(1)),
testing::Field(
&Observer::PacketsWrittenEvent::numAckElicitingPacketsWritten,
testing::Eq(1)),
testing::Field(
&Observer::PacketsWrittenEvent::numBytesWritten,
testing::Gt(bytesToWrite)));
// matcher for event from appRateLimited
const auto appRateLimitedMatcher = AllOf(
testing::Property(
&Observer::WriteEvent::getOutstandingPackets, testing::SizeIs(5)),
testing::Field(
&Observer::WriteEvent::writeCount, testing::Eq(writeNum)),
testing::Field(
&Observer::WriteEvent::maybeCwndInBytes,
testing::Eq(folly::Optional<uint64_t>(cwndInBytes))),
testing::Field( // precise check below
&Observer::WriteEvent::maybeWritableBytes,
testing::Lt(folly::Optional<uint64_t>(
upperBoundCurrentBytesWritable - bytesToWrite))));
invokeForEachObserverWithTestEvents(
([this, &startWritingFromAppLimitedMatcher](MockObserver& observer) {
EXPECT_CALL(
observer,
startWritingFromAppLimited(
transport_.get(), startWritingFromAppLimitedMatcher));
}));
invokeForEachObserverWithTestEvents(
([this,
&packetsWrittenMatcher,
oldTInfo = transport_->getTransportInfo()](MockObserver& observer) {
EXPECT_CALL(
observer, packetsWritten(transport_.get(), packetsWrittenMatcher))
.WillOnce(([oldTInfo](const auto& socket, const auto& event) {
EXPECT_EQ(
oldTInfo.writableBytes -
(socket->getTransportInfo().bytesSent -
oldTInfo.bytesSent),
event.maybeWritableBytes);
}));
}));
invokeForEachObserverWithTestEvents(
([this,
&appRateLimitedMatcher,
oldTInfo = transport_->getTransportInfo()](MockObserver& observer) {
EXPECT_CALL(
observer, appRateLimited(transport_.get(), appRateLimitedMatcher))
.WillOnce(([oldTInfo](const auto& socket, const auto& event) {
EXPECT_EQ(
oldTInfo.writableBytes -
(socket->getTransportInfo().bytesSent -
oldTInfo.bytesSent),
event.maybeWritableBytes);
}));
}));
auto stream = transport_->createBidirectionalStream().value();
transport_->writeChain(
stream, buildRandomInputData(bytesToWrite), false, nullptr);
transport_->updateWriteLooper(true);
loopForWrites();
loopForWrites();
// remove bytesToWrite from upperBoundCurrentBytesWritable
upperBoundCurrentBytesWritable -= bytesToWrite;
writeNum++;
}
invokeForAllObservers(([this](MockObserver& observer) {
EXPECT_CALL(observer, close(transport_.get(), _));
}));
invokeForAllObservers(([this](MockObserver& observer) {
EXPECT_CALL(observer, destroy(transport_.get()));
}));
transport_->close(folly::none);
transport_ = nullptr;
}
TEST_F(QuicTransportTest, ObserverStreamEventBidirectionalLocalOpenClose) { TEST_F(QuicTransportTest, ObserverStreamEventBidirectionalLocalOpenClose) {
Observer::Config configWithStreamEvents = {}; Observer::Config configWithStreamEvents = {};
configWithStreamEvents.streamEvents = true; configWithStreamEvents.streamEvents = true;

View File

@@ -7,11 +7,13 @@
#include <gmock/gmock.h> #include <gmock/gmock.h>
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <algorithm>
#include <chrono> #include <chrono>
#include <quic/api/test/Mocks.h> #include <quic/api/test/Mocks.h>
#include <quic/api/test/QuicTypedTransportTestUtil.h> #include <quic/api/test/QuicTypedTransportTestUtil.h>
#include <quic/codec/Types.h> #include <quic/codec/Types.h>
#include <quic/congestion_control/StaticCwndCongestionController.h>
#include <quic/fizz/client/test/QuicClientTransportTestUtil.h> #include <quic/fizz/client/test/QuicClientTransportTestUtil.h>
#include <quic/server/test/QuicServerTransportTestUtil.h> #include <quic/server/test/QuicServerTransportTestUtil.h>
#include <quic/state/AckEvent.h> #include <quic/state/AckEvent.h>
@@ -94,7 +96,7 @@ TYPED_TEST(QuicTypedTransportTest, TransportInfoMinRtt) {
{ {
// get the packet's send time // get the packet's send time
const auto packetSentTime = const auto packetSentTime =
CHECK_NOTNULL(this->getLastAppDataPacketWritten())->metadata.time; CHECK_NOTNULL(this->getNewestAppDataOutstandingPacket())->metadata.time;
// deliver an ACK for the outstanding packet 31 ms later // deliver an ACK for the outstanding packet 31 ms later
const auto packetAckTime = packetSentTime + 31ms; const auto packetAckTime = packetSentTime + 31ms;
@@ -126,7 +128,7 @@ TYPED_TEST(QuicTypedTransportTest, TransportInfoMinRtt) {
{ {
// get the packet's send time // get the packet's send time
const auto packetSentTime = const auto packetSentTime =
CHECK_NOTNULL(this->getLastAppDataPacketWritten())->metadata.time; CHECK_NOTNULL(this->getNewestAppDataOutstandingPacket())->metadata.time;
// deliver an ACK for the outstanding packet 42 ms later // deliver an ACK for the outstanding packet 42 ms later
const auto packetAckTime = packetSentTime + 42ms; const auto packetAckTime = packetSentTime + 42ms;
@@ -158,7 +160,7 @@ TYPED_TEST(QuicTypedTransportTest, TransportInfoMinRtt) {
{ {
// get the packet's send time // get the packet's send time
const auto packetSentTime = const auto packetSentTime =
CHECK_NOTNULL(this->getLastAppDataPacketWritten())->metadata.time; CHECK_NOTNULL(this->getNewestAppDataOutstandingPacket())->metadata.time;
// deliver an ACK for the outstanding packet 19 ms later // deliver an ACK for the outstanding packet 19 ms later
const auto packetAckTime = packetSentTime + 19ms; const auto packetAckTime = packetSentTime + 19ms;
@@ -1170,6 +1172,568 @@ TYPED_TEST(
this->destroyTransport(); this->destroyTransport();
} }
TYPED_TEST(
QuicTypedTransportTestForObservers,
WriteEventsOutstandingPacketSent) {
InSequence s;
// ACK outstanding packets so that we can switch out the congestion control
this->ackAllOutstandingPackets();
EXPECT_THAT(this->getConn().outstandings.packets, IsEmpty());
// determine the starting writeCount
auto writeCount = this->getConn().writeCount;
// install StaticCwndCongestionController
const auto cwndInBytes = 10000;
this->getNonConstConn().congestionController =
std::make_unique<StaticCwndCongestionController>(
StaticCwndCongestionController::CwndInBytes(cwndInBytes));
MockObserver::Config configWithEventsEnabled;
configWithEventsEnabled.appRateLimitedEvents = true;
configWithEventsEnabled.packetsWrittenEvents = true;
auto transport = this->getTransport();
auto observerWithNoEvents = std::make_unique<NiceMock<MockObserver>>();
auto observerWithEvents1 =
std::make_unique<NiceMock<MockObserver>>(configWithEventsEnabled);
auto observerWithEvents2 =
std::make_unique<NiceMock<MockObserver>>(configWithEventsEnabled);
EXPECT_CALL(*observerWithNoEvents, observerAttach(transport));
transport->addObserver(observerWithNoEvents.get());
EXPECT_CALL(*observerWithEvents1, observerAttach(transport));
transport->addObserver(observerWithEvents1.get());
EXPECT_CALL(*observerWithEvents2, observerAttach(transport));
transport->addObserver(observerWithEvents2.get());
EXPECT_THAT(
transport->getObservers(),
UnorderedElementsAre(
observerWithNoEvents.get(),
observerWithEvents1.get(),
observerWithEvents2.get()));
// string to write
const std::string str1 = "hello";
const auto strLength = str1.length();
// open stream that we will write to
const auto streamId =
this->getTransport()->createBidirectionalStream().value();
// setup matchers
{
writeCount++; // write count will be incremented
// matcher for event from startWritingFromAppLimited
const auto startWritingFromAppLimitedMatcher = AllOf(
testing::Property(
&Observer::WriteEvent::getOutstandingPackets, testing::IsEmpty()),
testing::Field(
&Observer::WriteEvent::writeCount, testing::Eq(writeCount)),
testing::Field(
&Observer::WriteEvent::maybeCwndInBytes,
testing::Eq(folly::Optional<uint64_t>(cwndInBytes))),
testing::Field(
&Observer::WriteEvent::maybeWritableBytes,
testing::Eq(folly::Optional<uint64_t>(cwndInBytes))));
EXPECT_CALL(*observerWithNoEvents, startWritingFromAppLimited(_, _))
.Times(0);
EXPECT_CALL(
*observerWithEvents1,
startWritingFromAppLimited(
transport, startWritingFromAppLimitedMatcher));
EXPECT_CALL(
*observerWithEvents2,
startWritingFromAppLimited(
transport, startWritingFromAppLimitedMatcher));
// matcher for event from packetsWritten
const auto packetsWrittenMatcher = AllOf(
testing::Property(
&Observer::WriteEvent::getOutstandingPackets, testing::SizeIs(1)),
testing::Field(
&Observer::WriteEvent::writeCount, testing::Eq(writeCount)),
testing::Field(
&Observer::WriteEvent::maybeCwndInBytes,
testing::Eq(folly::Optional<uint64_t>(cwndInBytes))),
testing::Field( // precise check in WillOnce()
&Observer::WriteEvent::maybeWritableBytes,
testing::Lt(folly::Optional<uint64_t>(cwndInBytes - strLength))),
testing::Field(
&Observer::PacketsWrittenEvent::numPacketsWritten, testing::Eq(1)),
testing::Field(
&Observer::PacketsWrittenEvent::numAckElicitingPacketsWritten,
testing::Eq(1)),
testing::Field( // precise check in WillOnce()
&Observer::PacketsWrittenEvent::numBytesWritten,
testing::Gt(strLength)));
EXPECT_CALL(*observerWithNoEvents, packetsWritten(_, _)).Times(0);
EXPECT_CALL(
*observerWithEvents1, packetsWritten(transport, packetsWrittenMatcher))
.WillOnce([oldTInfo = this->getTransport()->getTransportInfo()](
const auto& socket, const auto& event) {
const auto bytesWritten =
socket->getTransportInfo().bytesSent - oldTInfo.bytesSent;
EXPECT_EQ(
folly::Optional<uint64_t>(std::max(
int64_t(cwndInBytes) - int64_t(bytesWritten), int64_t(0))),
event.maybeWritableBytes);
EXPECT_EQ(bytesWritten, event.numBytesWritten);
});
EXPECT_CALL(
*observerWithEvents2, packetsWritten(transport, packetsWrittenMatcher))
.WillOnce([oldTInfo = this->getTransport()->getTransportInfo()](
const auto& socket, const auto& event) {
const auto bytesWritten =
socket->getTransportInfo().bytesSent - oldTInfo.bytesSent;
EXPECT_EQ(
folly::Optional<uint64_t>(std::max(
int64_t(cwndInBytes) - int64_t(bytesWritten), int64_t(0))),
event.maybeWritableBytes);
EXPECT_EQ(bytesWritten, event.numBytesWritten);
});
// matcher for event from appRateLimited
const auto appRateLimitedMatcher = AllOf(
testing::Property(
&Observer::WriteEvent::getOutstandingPackets, testing::SizeIs(1)),
testing::Field(
&Observer::WriteEvent::writeCount, testing::Eq(writeCount)),
testing::Field(
&Observer::WriteEvent::maybeCwndInBytes,
testing::Eq(folly::Optional<uint64_t>(cwndInBytes))),
testing::Field( // precise check below
&Observer::WriteEvent::maybeWritableBytes,
testing::Lt(folly::Optional<uint64_t>(cwndInBytes - strLength))));
EXPECT_CALL(*observerWithNoEvents, appRateLimited(_, _)).Times(0);
EXPECT_CALL(
*observerWithEvents1, appRateLimited(transport, appRateLimitedMatcher))
.WillOnce([oldTInfo = this->getTransport()->getTransportInfo()](
const auto& socket, const auto& event) {
const auto bytesWritten =
socket->getTransportInfo().bytesSent - oldTInfo.bytesSent;
EXPECT_EQ(
folly::Optional<uint64_t>(std::max(
int64_t(cwndInBytes) - int64_t(bytesWritten), int64_t(0))),
event.maybeWritableBytes);
});
EXPECT_CALL(
*observerWithEvents2, appRateLimited(transport, appRateLimitedMatcher))
.WillOnce([oldTInfo = this->getTransport()->getTransportInfo()](
const auto& socket, const auto& event) {
const auto bytesWritten =
socket->getTransportInfo().bytesSent - oldTInfo.bytesSent;
EXPECT_EQ(
folly::Optional<uint64_t>(std::max(
int64_t(cwndInBytes) - int64_t(bytesWritten), int64_t(0))),
event.maybeWritableBytes);
});
}
// open a stream and write string
{
this->getTransport()->writeChain(streamId, IOBuf::copyBuffer(str1), false);
const auto maybeWrittenPackets = this->loopForWrites();
// should have sent one packet
ASSERT_TRUE(maybeWrittenPackets.has_value());
quic::PacketNum firstPacketNum = maybeWrittenPackets->start;
quic::PacketNum lastPacketNum = maybeWrittenPackets->end;
EXPECT_EQ(1, lastPacketNum - firstPacketNum + 1);
}
this->destroyTransport();
}
TYPED_TEST(
QuicTypedTransportTestForObservers,
WriteEventsOutstandingPacketSentWroteMoreThanCwnd) {
InSequence s;
// ACK outstanding packets so that we can switch out the congestion control
this->ackAllOutstandingPackets();
EXPECT_THAT(this->getConn().outstandings.packets, IsEmpty());
// determine the starting writeCount
auto writeCount = this->getConn().writeCount;
// install StaticCwndCongestionController with a CWND < MSS
const auto cwndInBytes = 800;
this->getNonConstConn().congestionController =
std::make_unique<StaticCwndCongestionController>(
StaticCwndCongestionController::CwndInBytes(cwndInBytes));
MockObserver::Config configWithEventsEnabled;
configWithEventsEnabled.appRateLimitedEvents = true;
configWithEventsEnabled.packetsWrittenEvents = true;
auto transport = this->getTransport();
auto observerWithNoEvents = std::make_unique<NiceMock<MockObserver>>();
auto observerWithEvents1 =
std::make_unique<NiceMock<MockObserver>>(configWithEventsEnabled);
auto observerWithEvents2 =
std::make_unique<NiceMock<MockObserver>>(configWithEventsEnabled);
EXPECT_CALL(*observerWithNoEvents, observerAttach(transport));
transport->addObserver(observerWithNoEvents.get());
EXPECT_CALL(*observerWithEvents1, observerAttach(transport));
transport->addObserver(observerWithEvents1.get());
EXPECT_CALL(*observerWithEvents2, observerAttach(transport));
transport->addObserver(observerWithEvents2.get());
EXPECT_THAT(
transport->getObservers(),
UnorderedElementsAre(
observerWithNoEvents.get(),
observerWithEvents1.get(),
observerWithEvents2.get()));
// we're going to write 1000 bytes with a smaller CWND
// because MSS > CWND, we're going to overshoot
const auto bufLength = 1000;
auto buf = buildRandomInputData(bufLength);
EXPECT_GT(bufLength, cwndInBytes);
// open stream that we will write to
const auto streamId =
this->getTransport()->createBidirectionalStream().value();
// setup matchers
{
writeCount++; // write count will be incremented
// matcher for event from startWritingFromAppLimited
const auto startWritingFromAppLimitedMatcher = AllOf(
testing::Property(
&Observer::WriteEvent::getOutstandingPackets, testing::IsEmpty()),
testing::Field(
&Observer::WriteEvent::writeCount, testing::Eq(writeCount)),
testing::Field(
&Observer::WriteEvent::maybeCwndInBytes,
testing::Eq(folly::Optional<uint64_t>(cwndInBytes))),
testing::Field(
&Observer::WriteEvent::maybeWritableBytes,
testing::Eq(folly::Optional<uint64_t>(cwndInBytes))));
EXPECT_CALL(*observerWithNoEvents, startWritingFromAppLimited(_, _))
.Times(0);
EXPECT_CALL(
*observerWithEvents1,
startWritingFromAppLimited(
transport, startWritingFromAppLimitedMatcher));
EXPECT_CALL(
*observerWithEvents2,
startWritingFromAppLimited(
transport, startWritingFromAppLimitedMatcher));
// matcher for event from packetsWritten
const auto packetsWrittenMatcher = AllOf(
testing::Property(
&Observer::WriteEvent::getOutstandingPackets, testing::SizeIs(1)),
testing::Field(
&Observer::WriteEvent::writeCount, testing::Eq(writeCount)),
testing::Field(
&Observer::WriteEvent::maybeCwndInBytes,
testing::Eq(folly::Optional<uint64_t>(cwndInBytes))),
testing::Field( // precise check in WillOnce()
&Observer::WriteEvent::maybeWritableBytes,
testing::Eq(folly::Optional<uint64_t>(0))),
testing::Field(
&Observer::PacketsWrittenEvent::numPacketsWritten, testing::Eq(1)),
testing::Field(
&Observer::PacketsWrittenEvent::numAckElicitingPacketsWritten,
testing::Eq(1)),
testing::Field( // precise check in WillOnce(), expect overshoot CWND
&Observer::PacketsWrittenEvent::numBytesWritten,
testing::AllOf(testing::Gt(bufLength), testing::Gt(cwndInBytes))));
EXPECT_CALL(*observerWithNoEvents, packetsWritten(_, _)).Times(0);
EXPECT_CALL(
*observerWithEvents1, packetsWritten(transport, packetsWrittenMatcher))
.WillOnce([oldTInfo = this->getTransport()->getTransportInfo()](
const auto& socket, const auto& event) {
const auto bytesWritten =
socket->getTransportInfo().bytesSent - oldTInfo.bytesSent;
EXPECT_EQ(
folly::Optional<uint64_t>(std::max(
int64_t(cwndInBytes) - int64_t(bytesWritten), int64_t(0))),
event.maybeWritableBytes);
EXPECT_EQ(bytesWritten, event.numBytesWritten);
});
EXPECT_CALL(
*observerWithEvents2, packetsWritten(transport, packetsWrittenMatcher))
.WillOnce([oldTInfo = this->getTransport()->getTransportInfo()](
const auto& socket, const auto& event) {
const auto bytesWritten =
socket->getTransportInfo().bytesSent - oldTInfo.bytesSent;
EXPECT_EQ(
folly::Optional<uint64_t>(std::max(
int64_t(cwndInBytes) - int64_t(bytesWritten), int64_t(0))),
event.maybeWritableBytes);
EXPECT_EQ(bytesWritten, event.numBytesWritten);
});
}
// open a stream and write string
{
this->getTransport()->writeChain(streamId, std::move(buf), false);
const auto maybeWrittenPackets = this->loopForWrites();
// should have sent one packet
ASSERT_TRUE(maybeWrittenPackets.has_value());
quic::PacketNum firstPacketNum = maybeWrittenPackets->start;
quic::PacketNum lastPacketNum = maybeWrittenPackets->end;
EXPECT_EQ(1, lastPacketNum - firstPacketNum + 1);
}
// TODO(bschlinker): Check for appRateLimited on ACK so that we get an
// appRateLimited signal when the outstanding packet is ACKed.
this->destroyTransport();
}
TYPED_TEST(
QuicTypedTransportTestForObservers,
WriteEventsOutstandingPacketsSentCwndLimited) {
InSequence s;
// ACK outstanding packets so that we can switch out the congestion control
this->ackAllOutstandingPackets();
EXPECT_THAT(this->getConn().outstandings.packets, IsEmpty());
// determine the starting writeCount
auto writeCount = this->getConn().writeCount;
// install StaticCwndCongestionController
const auto cwndInBytes = 7000;
this->getNonConstConn().congestionController =
std::make_unique<StaticCwndCongestionController>(
StaticCwndCongestionController::CwndInBytes(cwndInBytes));
MockObserver::Config configWithEventsEnabled;
configWithEventsEnabled.appRateLimitedEvents = true;
configWithEventsEnabled.packetsWrittenEvents = true;
auto transport = this->getTransport();
auto observerWithNoEvents = std::make_unique<NiceMock<MockObserver>>();
auto observerWithEvents1 =
std::make_unique<NiceMock<MockObserver>>(configWithEventsEnabled);
auto observerWithEvents2 =
std::make_unique<NiceMock<MockObserver>>(configWithEventsEnabled);
EXPECT_CALL(*observerWithNoEvents, observerAttach(transport));
transport->addObserver(observerWithNoEvents.get());
EXPECT_CALL(*observerWithEvents1, observerAttach(transport));
transport->addObserver(observerWithEvents1.get());
EXPECT_CALL(*observerWithEvents2, observerAttach(transport));
transport->addObserver(observerWithEvents2.get());
EXPECT_THAT(
transport->getObservers(),
UnorderedElementsAre(
observerWithNoEvents.get(),
observerWithEvents1.get(),
observerWithEvents2.get()));
// open stream that we will write to
const auto streamId =
this->getTransport()->createBidirectionalStream().value();
// we're going to write 10000 bytes with a CWND of 7000
const auto bufLength = 10000;
auto buf = buildRandomInputData(bufLength);
EXPECT_EQ(7000, cwndInBytes);
// setup matchers for first write, write the entire buffer, trigger loop
// we will NOT become app limited after this write, as CWND limited
{
writeCount++; // write count will be incremented
const auto packetsExpectedWritten = 5;
// matcher for event from startWritingFromAppLimited
const auto startWritingFromAppLimitedMatcher = AllOf(
testing::Property(
&Observer::WriteEvent::getOutstandingPackets, testing::IsEmpty()),
testing::Field(
&Observer::WriteEvent::writeCount, testing::Eq(writeCount)),
testing::Field(
&Observer::WriteEvent::maybeCwndInBytes,
testing::Eq(folly::Optional<uint64_t>(cwndInBytes))),
testing::Field(
&Observer::WriteEvent::maybeWritableBytes,
testing::Eq(folly::Optional<uint64_t>(cwndInBytes))));
EXPECT_CALL(*observerWithNoEvents, startWritingFromAppLimited(_, _))
.Times(0);
EXPECT_CALL(
*observerWithEvents1,
startWritingFromAppLimited(
transport, startWritingFromAppLimitedMatcher));
EXPECT_CALL(
*observerWithEvents2,
startWritingFromAppLimited(
transport, startWritingFromAppLimitedMatcher));
// matcher for event from packetsWritten
const auto packetsWrittenMatcher = AllOf(
testing::Property(
&Observer::WriteEvent::getOutstandingPackets,
testing::SizeIs(packetsExpectedWritten)),
testing::Field(
&Observer::WriteEvent::writeCount, testing::Eq(writeCount)),
testing::Field(
&Observer::WriteEvent::maybeCwndInBytes,
testing::Eq(folly::Optional<uint64_t>(cwndInBytes))),
testing::Field( // precise check in WillOnce()
&Observer::WriteEvent::maybeWritableBytes,
testing::Eq(folly::Optional<uint64_t>(0))), // CWND exhausted
testing::Field(
&Observer::PacketsWrittenEvent::numPacketsWritten,
testing::Eq(packetsExpectedWritten)),
testing::Field(
&Observer::PacketsWrittenEvent::numAckElicitingPacketsWritten,
testing::Eq(packetsExpectedWritten)),
testing::Field( // precise check in WillOnce()
&Observer::PacketsWrittenEvent::numBytesWritten,
testing::Ge(cwndInBytes))); // full CWND written
EXPECT_CALL(*observerWithNoEvents, packetsWritten(_, _)).Times(0);
EXPECT_CALL(
*observerWithEvents1, packetsWritten(transport, packetsWrittenMatcher))
.WillOnce([oldTInfo = this->getTransport()->getTransportInfo()](
const auto& socket, const auto& event) {
const auto bytesWritten =
socket->getTransportInfo().bytesSent - oldTInfo.bytesSent;
EXPECT_EQ(
folly::Optional<uint64_t>(std::max(
int64_t(cwndInBytes) - int64_t(bytesWritten), int64_t(0))),
event.maybeWritableBytes);
EXPECT_EQ(bytesWritten, event.numBytesWritten);
});
EXPECT_CALL(
*observerWithEvents2, packetsWritten(transport, packetsWrittenMatcher))
.WillOnce([oldTInfo = this->getTransport()->getTransportInfo()](
const auto& socket, const auto& event) {
const auto bytesWritten =
socket->getTransportInfo().bytesSent - oldTInfo.bytesSent;
EXPECT_EQ(
folly::Optional<uint64_t>(std::max(
int64_t(cwndInBytes) - int64_t(bytesWritten), int64_t(0))),
event.maybeWritableBytes);
EXPECT_EQ(bytesWritten, event.numBytesWritten);
});
this->getTransport()->writeChain(streamId, std::move(buf), false);
const auto maybeWrittenPackets = this->loopForWrites();
// make sure we wrote
ASSERT_TRUE(maybeWrittenPackets.has_value());
quic::PacketNum firstPacketNum = maybeWrittenPackets->start;
quic::PacketNum lastPacketNum = maybeWrittenPackets->end;
EXPECT_EQ(packetsExpectedWritten, lastPacketNum - firstPacketNum + 1);
}
// ACK all outstanding packets
this->ackAllOutstandingPackets();
// setup matchers for second write, then trigger loop
// we will become app limited after this write
{
writeCount++; // write count will be incremented
const auto packetsExpectedWritten = 2;
// matcher for event from packetsWritten
const auto packetsWrittenMatcher = AllOf(
testing::Property(
&Observer::WriteEvent::getOutstandingPackets,
testing::SizeIs(packetsExpectedWritten)),
testing::Field(
&Observer::WriteEvent::writeCount, testing::Eq(writeCount)),
testing::Field(
&Observer::WriteEvent::maybeCwndInBytes,
testing::Eq(folly::Optional<uint64_t>(cwndInBytes))),
testing::Field( // precise check in WillOnce()
&Observer::WriteEvent::maybeWritableBytes,
testing::Lt(folly::Optional<uint64_t>(cwndInBytes))),
testing::Field(
&Observer::PacketsWrittenEvent::numPacketsWritten,
testing::Eq(packetsExpectedWritten)),
testing::Field(
&Observer::PacketsWrittenEvent::numAckElicitingPacketsWritten,
testing::Eq(packetsExpectedWritten)),
testing::Field( // precise check in WillOnce()
&Observer::PacketsWrittenEvent::numBytesWritten,
testing::Lt(cwndInBytes)));
EXPECT_CALL(*observerWithNoEvents, packetsWritten(_, _)).Times(0);
EXPECT_CALL(
*observerWithEvents1, packetsWritten(transport, packetsWrittenMatcher))
.WillOnce([oldTInfo = this->getTransport()->getTransportInfo()](
const auto& socket, const auto& event) {
const auto bytesWritten =
socket->getTransportInfo().bytesSent - oldTInfo.bytesSent;
EXPECT_EQ(
folly::Optional<uint64_t>(std::max(
int64_t(cwndInBytes) - int64_t(bytesWritten), int64_t(0))),
event.maybeWritableBytes);
EXPECT_EQ(bytesWritten, event.numBytesWritten);
});
EXPECT_CALL(
*observerWithEvents2, packetsWritten(transport, packetsWrittenMatcher))
.WillOnce([oldTInfo = this->getTransport()->getTransportInfo()](
const auto& socket, const auto& event) {
const auto bytesWritten =
socket->getTransportInfo().bytesSent - oldTInfo.bytesSent;
EXPECT_EQ(
folly::Optional<uint64_t>(std::max(
int64_t(cwndInBytes) - int64_t(bytesWritten), int64_t(0))),
event.maybeWritableBytes);
EXPECT_EQ(bytesWritten, event.numBytesWritten);
});
// matcher for event from appRateLimited
const auto appRateLimitedMatcher = AllOf(
testing::Property(
&Observer::WriteEvent::getOutstandingPackets,
testing::SizeIs(packetsExpectedWritten)),
testing::Field(
&Observer::WriteEvent::writeCount, testing::Eq(writeCount)),
testing::Field(
&Observer::WriteEvent::maybeCwndInBytes,
testing::Eq(folly::Optional<uint64_t>(cwndInBytes))),
testing::Field( // precise check in WillOnce()
&Observer::WriteEvent::maybeWritableBytes,
testing::Lt(folly::Optional<uint64_t>(cwndInBytes))));
EXPECT_CALL(*observerWithNoEvents, appRateLimited(_, _)).Times(0);
EXPECT_CALL(
*observerWithEvents1, appRateLimited(transport, appRateLimitedMatcher))
.WillOnce([oldTInfo = this->getTransport()->getTransportInfo()](
const auto& socket, const auto& event) {
const auto bytesWritten =
socket->getTransportInfo().bytesSent - oldTInfo.bytesSent;
EXPECT_EQ(
folly::Optional<uint64_t>(std::max(
int64_t(cwndInBytes) - int64_t(bytesWritten), int64_t(0))),
event.maybeWritableBytes);
});
EXPECT_CALL(
*observerWithEvents2, appRateLimited(transport, appRateLimitedMatcher))
.WillOnce([oldTInfo = this->getTransport()->getTransportInfo()](
const auto& socket, const auto& event) {
const auto bytesWritten =
socket->getTransportInfo().bytesSent - oldTInfo.bytesSent;
EXPECT_EQ(
folly::Optional<uint64_t>(std::max(
int64_t(cwndInBytes) - int64_t(bytesWritten), int64_t(0))),
event.maybeWritableBytes);
});
const auto maybeWrittenPackets = this->loopForWrites();
ASSERT_TRUE(maybeWrittenPackets.has_value());
quic::PacketNum firstPacketNum = maybeWrittenPackets->start;
quic::PacketNum lastPacketNum = maybeWrittenPackets->end;
EXPECT_EQ(packetsExpectedWritten, lastPacketNum - firstPacketNum + 1);
}
this->destroyTransport();
}
TYPED_TEST( TYPED_TEST(
QuicTypedTransportTestForObservers, QuicTypedTransportTestForObservers,
AckEventsOutstandingPacketSentThenAcked) { AckEventsOutstandingPacketSentThenAcked) {

View File

@@ -85,18 +85,40 @@ class QuicTypedTransportTestBase : protected QuicTransportTestClass {
} }
/** /**
* Returns the last outstanding packet written of the specified type. * Returns the first outstanding packet written of the specified type.
*
* If no outstanding packets of the specified type, returns nullptr.
* *
* Since this is a reference to a packet in the outstanding packets deque, it * Since this is a reference to a packet in the outstanding packets deque, it
* should not be stored. * should not be stored.
*/ */
const OutstandingPacket* FOLLY_NULLABLE const OutstandingPacket* FOLLY_NULLABLE
getLastPacketWritten(const quic::PacketNumberSpace packetNumberSpace) { getOldestOutstandingPacket(const quic::PacketNumberSpace packetNumberSpace) {
const auto outstandingPacketIt =
getFirstOutstandingPacket(this->getNonConstConn(), packetNumberSpace);
if (outstandingPacketIt ==
this->getNonConstConn().outstandings.packets.end()) {
return nullptr;
}
return &*outstandingPacketIt;
}
/**
* Returns the last outstanding packet written of the specified type.
*
* If no outstanding packets of the specified type, returns nullptr.
*
* Since this is a reference to a packet in the outstanding packets deque, it
* should not be stored.
*/
const OutstandingPacket* FOLLY_NULLABLE
getNewestOutstandingPacket(const quic::PacketNumberSpace packetNumberSpace) {
const auto outstandingPacketIt = const auto outstandingPacketIt =
getLastOutstandingPacket(this->getNonConstConn(), packetNumberSpace); getLastOutstandingPacket(this->getNonConstConn(), packetNumberSpace);
CHECK( if (outstandingPacketIt ==
outstandingPacketIt != this->getNonConstConn().outstandings.packets.rend()) {
this->getNonConstConn().outstandings.packets.rend()); return nullptr;
}
return &*outstandingPacketIt; return &*outstandingPacketIt;
} }
@@ -108,8 +130,41 @@ class QuicTypedTransportTestBase : protected QuicTransportTestClass {
* Since this is a reference to a packet in the outstanding packets deque, it * Since this is a reference to a packet in the outstanding packets deque, it
* should not be stored. * should not be stored.
*/ */
const OutstandingPacket* FOLLY_NULLABLE getLastAppDataPacketWritten() { const OutstandingPacket* FOLLY_NULLABLE getNewestAppDataOutstandingPacket() {
return getLastPacketWritten(PacketNumberSpace::AppData); return getNewestOutstandingPacket(PacketNumberSpace::AppData);
}
/**
* Acks all outstanding packets for the specified packet number space.
*/
void ackAllOutstandingPackets(
quic::PacketNumberSpace pnSpace,
quic::TimePoint recvTime = TimePoint::clock::now()) {
auto oldestOutstandingPkt = getOldestOutstandingPacket(pnSpace);
auto newestOutstandingPkt = getNewestOutstandingPacket(pnSpace);
CHECK_EQ(oldestOutstandingPkt == nullptr, newestOutstandingPkt == nullptr);
if (!oldestOutstandingPkt) {
return;
}
QuicTransportTestClass::deliverData(
NetworkData(
buildAckPacketForSentPackets(
pnSpace,
oldestOutstandingPkt->packet.header.getPacketSequenceNum(),
newestOutstandingPkt->packet.header.getPacketSequenceNum()),
recvTime),
false /* loopForWrites */);
}
/**
* Acks all outstanding packets for all packet number spaces.
*/
void ackAllOutstandingPackets(
quic::TimePoint recvTime = TimePoint::clock::now()) {
ackAllOutstandingPackets(quic::PacketNumberSpace::Initial, recvTime);
ackAllOutstandingPackets(quic::PacketNumberSpace::Handshake, recvTime);
ackAllOutstandingPackets(quic::PacketNumberSpace::AppData, recvTime);
} }
/** /**
@@ -222,6 +277,52 @@ class QuicTypedTransportTestBase : protected QuicTransportTestClass {
return buf; return buf;
} }
/**
* Build a packet from peer with ACK frame for previously sent packets.
*/
quic::Buf buildAckPacketForSentPackets(
quic::PacketNumberSpace pnSpace,
quic::AckBlocks acks) {
quic::PacketNum peerPacketNum{0};
switch (pnSpace) {
case quic::PacketNumberSpace::Initial:
peerPacketNum = peerNextInitialPacketNum;
peerNextInitialPacketNum++;
break;
case quic::PacketNumberSpace::Handshake:
peerPacketNum = peerNextHandshakePacketNum;
peerNextHandshakePacketNum++;
break;
case quic::PacketNumberSpace::AppData:
peerPacketNum = peerNextAppDataPacketNum;
peerNextAppDataPacketNum++;
break;
}
auto buf = quic::test::packetToBuf(quic::test::createAckPacket(
getNonConstConn(), peerPacketNum, acks, pnSpace));
buf->coalesce();
return buf;
}
/**
* Build a packet from peer with ACK frame for previously sent packets.
*/
quic::Buf buildAckPacketForSentPackets(
quic::PacketNumberSpace pnSpace,
quic::PacketNum intervalStart,
quic::PacketNum intervalEnd) {
quic::AckBlocks acks = {{intervalStart, intervalEnd}};
return buildAckPacketForSentPackets(pnSpace, acks);
}
/**
* Build a packet from peer with ACK frame for previously sent AppData pkts.
*/
quic::Buf buildAckPacketForSentAppDataPackets(quic::AckBlocks acks) {
return buildAckPacketForSentPackets(quic::PacketNumberSpace::AppData, acks);
}
/** /**
* Build a packet with ACK frame for previously sent AppData packet. * Build a packet with ACK frame for previously sent AppData packet.
*/ */
@@ -230,19 +331,6 @@ class QuicTypedTransportTestBase : protected QuicTransportTestClass {
return buildAckPacketForSentAppDataPackets(acks); return buildAckPacketForSentAppDataPackets(acks);
} }
/**
* Build a packet from peer with ACK frame for previously AppData packets.
*/
quic::Buf buildAckPacketForSentAppDataPackets(quic::AckBlocks acks) {
auto buf = quic::test::packetToBuf(quic::test::createAckPacket(
getNonConstConn(),
++peerNextAppDataPacketNum,
acks,
quic::PacketNumberSpace::AppData));
buf->coalesce();
return buf;
}
/** /**
* Build a packet with ACK frame for previously sent AppData packets. * Build a packet with ACK frame for previously sent AppData packets.
*/ */
@@ -603,6 +691,8 @@ class QuicTypedTransportTestBase : protected QuicTransportTestClass {
return getPacketNumsFromIntervals(writeIntervals); return getPacketNumsFromIntervals(writeIntervals);
} }
quic::PacketNum peerNextInitialPacketNum{0};
quic::PacketNum peerNextHandshakePacketNum{0};
quic::PacketNum peerNextAppDataPacketNum{0}; quic::PacketNum peerNextAppDataPacketNum{0};
}; };

View File

@@ -181,8 +181,11 @@ class QuicServerTransportTestBase : public virtual testing::Test {
EXPECT_EQ( EXPECT_EQ(
*server->getConn().clientConnectionId, *server->getConn().clientConnectionId,
server->getConn().peerConnectionIds[0].connId); server->getConn().peerConnectionIds[0].connId);
SetUpChild();
} }
virtual void SetUpChild() {}
void destroyTransport() { void destroyTransport() {
server = nullptr; server = nullptr;
} }