diff --git a/quic/api/Observer.cpp b/quic/api/Observer.cpp index cc4b951e9..80da17073 100644 --- a/quic/api/Observer.cpp +++ b/quic/api/Observer.cpp @@ -38,6 +38,18 @@ Observer::WriteEvent::Builder::setLastPacketSentTime( return std::move(*this); } +Observer::WriteEvent::Builder&& Observer::WriteEvent::Builder::setCwndInBytes( + const folly::Optional& maybeCwndInBytesIn) { + maybeCwndInBytes = maybeCwndInBytesIn; + return std::move(*this); +} + +Observer::WriteEvent::Builder&& Observer::WriteEvent::Builder::setWritableBytes( + const folly::Optional& maybeWritableBytesIn) { + maybeWritableBytes = maybeWritableBytesIn; + return std::move(*this); +} + Observer::WriteEvent Observer::WriteEvent::Builder::build() && { return WriteEvent(*this); } @@ -46,7 +58,9 @@ Observer::WriteEvent::WriteEvent(const WriteEvent::BuilderFields& builderFields) : outstandingPackets(*CHECK_NOTNULL( builderFields.maybeOutstandingPacketsRef.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::setOutstandingPackets( @@ -75,6 +89,20 @@ Observer::AppLimitedEvent::Builder::setLastPacketSentTime( return std::move(*this); } +Observer::AppLimitedEvent::Builder&& +Observer::AppLimitedEvent::Builder::setCwndInBytes( + const folly::Optional& maybeCwndInBytesIn) { + maybeCwndInBytes = maybeCwndInBytesIn; + return std::move(*this); +} + +Observer::AppLimitedEvent::Builder&& +Observer::AppLimitedEvent::Builder::setWritableBytes( + const folly::Optional& maybeWritableBytesIn) { + maybeWritableBytes = maybeWritableBytesIn; + return std::move(*this); +} + Observer::AppLimitedEvent Observer::AppLimitedEvent::Builder::build() && { return AppLimitedEvent(std::move(*this)); } @@ -111,6 +139,20 @@ Observer::PacketsWrittenEvent::Builder::setLastPacketSentTime( return std::move(*this); } +Observer::PacketsWrittenEvent::Builder&& +Observer::PacketsWrittenEvent::Builder::setCwndInBytes( + const folly::Optional& maybeCwndInBytesIn) { + maybeCwndInBytes = maybeCwndInBytesIn; + return std::move(*this); +} + +Observer::PacketsWrittenEvent::Builder&& +Observer::PacketsWrittenEvent::Builder::setWritableBytes( + const folly::Optional& maybeWritableBytesIn) { + maybeWritableBytes = maybeWritableBytesIn; + return std::move(*this); +} + Observer::PacketsWrittenEvent::Builder&& Observer::PacketsWrittenEvent::Builder::setNumPacketsWritten( const uint64_t numPacketsWrittenIn) { diff --git a/quic/api/Observer.h b/quic/api/Observer.h index 42aa498e5..026f79414 100644 --- a/quic/api/Observer.h +++ b/quic/api/Observer.h @@ -111,12 +111,24 @@ class Observer { // Timestamp when packet was last written const folly::Optional maybeLastPacketSentTime; + // CWND in bytes. + // + // Optional to handle cases where congestion controller not used. + const folly::Optional maybeCwndInBytes; + + // Writable bytes. + // + // Optional to handle cases where congestion controller not used. + const folly::Optional maybeWritableBytes; + struct BuilderFields { folly::Optional< std::reference_wrapper>> maybeOutstandingPacketsRef; folly::Optional maybeWriteCount; folly::Optional maybeLastPacketSentTime; + folly::Optional maybeCwndInBytes; + folly::Optional maybeWritableBytes; explicit BuilderFields() = default; }; @@ -127,6 +139,10 @@ class Observer { Builder&& setLastPacketSentTime(const TimePoint& lastPacketSentTimeIn); Builder&& setLastPacketSentTime( const folly::Optional& maybeLastPacketSentTimeIn); + Builder&& setCwndInBytes( + const folly::Optional& maybeCwndInBytesIn); + Builder&& setWritableBytes( + const folly::Optional& maybeWritableBytesIn); WriteEvent build() &&; explicit Builder() = default; }; @@ -149,6 +165,10 @@ class Observer { Builder&& setLastPacketSentTime(const TimePoint& lastPacketSentTimeIn); Builder&& setLastPacketSentTime( const folly::Optional& maybeLastPacketSentTimeIn); + Builder&& setCwndInBytes( + const folly::Optional& maybeCwndInBytesIn); + Builder&& setWritableBytes( + const folly::Optional& maybeWritableBytesIn); AppLimitedEvent build() &&; explicit Builder() = default; }; @@ -186,6 +206,10 @@ class Observer { Builder&& setNumAckElicitingPacketsWritten( const uint64_t numAckElicitingPacketsWrittenIn); Builder&& setNumBytesWritten(const uint64_t numBytesWrittenIn); + Builder&& setCwndInBytes( + const folly::Optional& maybeCwndInBytesIn); + Builder&& setWritableBytes( + const folly::Optional& maybeWritableBytesIn); PacketsWrittenEvent build() &&; explicit Builder() = default; }; diff --git a/quic/api/QuicTransportBase.cpp b/quic/api/QuicTransportBase.cpp index 8b672ea54..3a9128413 100644 --- a/quic/api/QuicTransportBase.cpp +++ b/quic/api/QuicTransportBase.cpp @@ -3547,6 +3547,16 @@ void QuicTransportBase::notifyStartWritingFromAppRateLimited() { .setOutstandingPackets(conn_->outstandings.packets) .setWriteCount(conn_->writeCount) .setLastPacketSentTime(conn_->lossState.maybeLastPacketSentTime) + .setCwndInBytes( + conn_->congestionController + ? folly::Optional( + conn_->congestionController->getCongestionWindow()) + : folly::none) + .setWritableBytes( + conn_->congestionController + ? folly::Optional( + conn_->congestionController->getWritableBytes()) + : folly::none) .build(); for (const auto& cb : *observers_) { if (cb->getConfig().appRateLimitedEvents) { @@ -3564,6 +3574,16 @@ void QuicTransportBase::notifyPacketsWritten( .setOutstandingPackets(conn_->outstandings.packets) .setWriteCount(conn_->writeCount) .setLastPacketSentTime(conn_->lossState.maybeLastPacketSentTime) + .setCwndInBytes( + conn_->congestionController + ? folly::Optional( + conn_->congestionController->getCongestionWindow()) + : folly::none) + .setWritableBytes( + conn_->congestionController + ? folly::Optional( + conn_->congestionController->getWritableBytes()) + : folly::none) .setNumPacketsWritten(numPacketsWritten) .setNumAckElicitingPacketsWritten(numAckElicitingPacketsWritten) .setNumBytesWritten(numBytesWritten) @@ -3581,6 +3601,16 @@ void QuicTransportBase::notifyAppRateLimited() { .setOutstandingPackets(conn_->outstandings.packets) .setWriteCount(conn_->writeCount) .setLastPacketSentTime(conn_->lossState.maybeLastPacketSentTime) + .setCwndInBytes( + conn_->congestionController + ? folly::Optional( + conn_->congestionController->getCongestionWindow()) + : folly::none) + .setWritableBytes( + conn_->congestionController + ? folly::Optional( + conn_->congestionController->getWritableBytes()) + : folly::none) .build(); for (const auto& cb : *observers_) { if (cb->getConfig().appRateLimitedEvents) { diff --git a/quic/api/test/QuicTransportTest.cpp b/quic/api/test/QuicTransportTest.cpp index df2aebeab..12c8a0bfb 100644 --- a/quic/api/test/QuicTransportTest.cpp +++ b/quic/api/test/QuicTransportTest.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -1060,6 +1061,266 @@ TEST_F(QuicTransportTest, ObserverPacketsWrittenCheckBytesSent) { transport_ = nullptr; } +TEST_F(QuicTransportTest, ObserverWriteEventsCheckCwndPacketsWritable) { + InSequence s; + + Observer::Config config = {}; + config.packetsWrittenEvents = true; + config.appRateLimitedEvents = true; + auto cb1 = std::make_unique>(config); + auto cb2 = std::make_unique>(config); + auto cb3 = std::make_unique>(Observer::Config()); + const auto invokeForAllObservers = + [&cb1, &cb2, &cb3](const std::function& fn) { + fn(*cb1); + fn(*cb2); + fn(*cb3); + }; + const auto invokeForEachObserverWithTestEvents = + [&cb1, &cb2](const std::function& 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::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(cwndInBytes))), + testing::Field( + &Observer::WriteEvent::maybeWritableBytes, + testing::Eq(folly::Optional(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(cwndInBytes))), + testing::Field( // precise check below + &Observer::WriteEvent::maybeWritableBytes, + testing::Lt(folly::Optional( + 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(cwndInBytes))), + testing::Field( // precise check below + &Observer::WriteEvent::maybeWritableBytes, + testing::Lt(folly::Optional( + 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(cwndInBytes))), + testing::Field( + &Observer::WriteEvent::maybeWritableBytes, + testing::Lt( + folly::Optional(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(cwndInBytes))), + testing::Field( // precise check below + &Observer::WriteEvent::maybeWritableBytes, + testing::Lt(folly::Optional( + 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(cwndInBytes))), + testing::Field( // precise check below + &Observer::WriteEvent::maybeWritableBytes, + testing::Lt(folly::Optional( + 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) { Observer::Config configWithStreamEvents = {}; configWithStreamEvents.streamEvents = true; diff --git a/quic/api/test/QuicTypedTransportTest.cpp b/quic/api/test/QuicTypedTransportTest.cpp index 90f5dfe26..9481e5b6b 100644 --- a/quic/api/test/QuicTypedTransportTest.cpp +++ b/quic/api/test/QuicTypedTransportTest.cpp @@ -7,11 +7,13 @@ #include #include +#include #include #include #include #include +#include #include #include #include @@ -94,7 +96,7 @@ TYPED_TEST(QuicTypedTransportTest, TransportInfoMinRtt) { { // get the packet's send time 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 const auto packetAckTime = packetSentTime + 31ms; @@ -126,7 +128,7 @@ TYPED_TEST(QuicTypedTransportTest, TransportInfoMinRtt) { { // get the packet's send time 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 const auto packetAckTime = packetSentTime + 42ms; @@ -158,7 +160,7 @@ TYPED_TEST(QuicTypedTransportTest, TransportInfoMinRtt) { { // get the packet's send time 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 const auto packetAckTime = packetSentTime + 19ms; @@ -1170,6 +1172,568 @@ TYPED_TEST( 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::CwndInBytes(cwndInBytes)); + + MockObserver::Config configWithEventsEnabled; + configWithEventsEnabled.appRateLimitedEvents = true; + configWithEventsEnabled.packetsWrittenEvents = true; + + auto transport = this->getTransport(); + auto observerWithNoEvents = std::make_unique>(); + auto observerWithEvents1 = + std::make_unique>(configWithEventsEnabled); + auto observerWithEvents2 = + std::make_unique>(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(cwndInBytes))), + testing::Field( + &Observer::WriteEvent::maybeWritableBytes, + testing::Eq(folly::Optional(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(cwndInBytes))), + testing::Field( // precise check in WillOnce() + &Observer::WriteEvent::maybeWritableBytes, + testing::Lt(folly::Optional(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(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(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(cwndInBytes))), + testing::Field( // precise check below + &Observer::WriteEvent::maybeWritableBytes, + testing::Lt(folly::Optional(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(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(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::CwndInBytes(cwndInBytes)); + + MockObserver::Config configWithEventsEnabled; + configWithEventsEnabled.appRateLimitedEvents = true; + configWithEventsEnabled.packetsWrittenEvents = true; + + auto transport = this->getTransport(); + auto observerWithNoEvents = std::make_unique>(); + auto observerWithEvents1 = + std::make_unique>(configWithEventsEnabled); + auto observerWithEvents2 = + std::make_unique>(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(cwndInBytes))), + testing::Field( + &Observer::WriteEvent::maybeWritableBytes, + testing::Eq(folly::Optional(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(cwndInBytes))), + testing::Field( // precise check in WillOnce() + &Observer::WriteEvent::maybeWritableBytes, + testing::Eq(folly::Optional(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(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(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::CwndInBytes(cwndInBytes)); + + MockObserver::Config configWithEventsEnabled; + configWithEventsEnabled.appRateLimitedEvents = true; + configWithEventsEnabled.packetsWrittenEvents = true; + + auto transport = this->getTransport(); + auto observerWithNoEvents = std::make_unique>(); + auto observerWithEvents1 = + std::make_unique>(configWithEventsEnabled); + auto observerWithEvents2 = + std::make_unique>(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(cwndInBytes))), + testing::Field( + &Observer::WriteEvent::maybeWritableBytes, + testing::Eq(folly::Optional(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(cwndInBytes))), + testing::Field( // precise check in WillOnce() + &Observer::WriteEvent::maybeWritableBytes, + testing::Eq(folly::Optional(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(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(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(cwndInBytes))), + testing::Field( // precise check in WillOnce() + &Observer::WriteEvent::maybeWritableBytes, + testing::Lt(folly::Optional(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(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(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(cwndInBytes))), + testing::Field( // precise check in WillOnce() + &Observer::WriteEvent::maybeWritableBytes, + testing::Lt(folly::Optional(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(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(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( QuicTypedTransportTestForObservers, AckEventsOutstandingPacketSentThenAcked) { diff --git a/quic/api/test/QuicTypedTransportTestUtil.h b/quic/api/test/QuicTypedTransportTestUtil.h index 46b05c5d7..d4fe75940 100644 --- a/quic/api/test/QuicTypedTransportTestUtil.h +++ b/quic/api/test/QuicTypedTransportTestUtil.h @@ -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 * should not be stored. */ 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 = getLastOutstandingPacket(this->getNonConstConn(), packetNumberSpace); - CHECK( - outstandingPacketIt != - this->getNonConstConn().outstandings.packets.rend()); + if (outstandingPacketIt == + this->getNonConstConn().outstandings.packets.rend()) { + return nullptr; + } return &*outstandingPacketIt; } @@ -108,8 +130,41 @@ class QuicTypedTransportTestBase : protected QuicTransportTestClass { * Since this is a reference to a packet in the outstanding packets deque, it * should not be stored. */ - const OutstandingPacket* FOLLY_NULLABLE getLastAppDataPacketWritten() { - return getLastPacketWritten(PacketNumberSpace::AppData); + const OutstandingPacket* FOLLY_NULLABLE getNewestAppDataOutstandingPacket() { + 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; } + /** + * 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. */ @@ -230,19 +331,6 @@ class QuicTypedTransportTestBase : protected QuicTransportTestClass { 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. */ @@ -603,6 +691,8 @@ class QuicTypedTransportTestBase : protected QuicTransportTestClass { return getPacketNumsFromIntervals(writeIntervals); } + quic::PacketNum peerNextInitialPacketNum{0}; + quic::PacketNum peerNextHandshakePacketNum{0}; quic::PacketNum peerNextAppDataPacketNum{0}; }; diff --git a/quic/server/test/QuicServerTransportTestUtil.h b/quic/server/test/QuicServerTransportTestUtil.h index 288bf0cea..7b4dd15ef 100644 --- a/quic/server/test/QuicServerTransportTestUtil.h +++ b/quic/server/test/QuicServerTransportTestUtil.h @@ -181,8 +181,11 @@ class QuicServerTransportTestBase : public virtual testing::Test { EXPECT_EQ( *server->getConn().clientConnectionId, server->getConn().peerConnectionIds[0].connId); + SetUpChild(); } + virtual void SetUpChild() {} + void destroyTransport() { server = nullptr; }