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:
committed by
Facebook GitHub Bot
parent
f30df88637
commit
da71a15307
@@ -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) {
|
||||||
|
@@ -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;
|
||||||
};
|
};
|
||||||
|
@@ -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) {
|
||||||
|
@@ -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;
|
||||||
|
@@ -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) {
|
||||||
|
@@ -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};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user