gfe-relnote: (n/a) Add unit tests for quic::MaxAckHeightTracker. Test only.
PiperOrigin-RevId: 272305887
Change-Id: I099e8b16d765c52e2e8c893c95911bce0f6e40c7
diff --git a/quic/core/congestion_control/bandwidth_sampler_test.cc b/quic/core/congestion_control/bandwidth_sampler_test.cc
index aeff976..558971a 100644
--- a/quic/core/congestion_control/bandwidth_sampler_test.cc
+++ b/quic/core/congestion_control/bandwidth_sampler_test.cc
@@ -4,6 +4,8 @@
#include "net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.h"
+#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
#include "net/third_party/quiche/src/quic/core/quic_types.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
@@ -462,5 +464,132 @@
EXPECT_EQ(0u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_));
}
+class MaxAckHeightTrackerTest : public QuicTest {
+ protected:
+ MaxAckHeightTrackerTest() : tracker_(/*initial_filter_window=*/10) {}
+
+ // Run a full aggregation episode, which is one or more aggregated acks,
+ // followed by a quiet period in which no ack happens.
+ // After this function returns, the time is set to the earliest point at which
+ // any ack event will cause tracker_.Update() to start a new aggregation.
+ void AggregationEpisode(QuicBandwidth aggregation_bandwidth,
+ QuicTime::Delta aggregation_duration,
+ QuicByteCount bytes_per_ack,
+ bool expect_new_aggregation_epoch) {
+ ASSERT_GE(aggregation_bandwidth, bandwidth_);
+ const QuicTime start_time = now_;
+
+ const QuicByteCount aggregation_bytes =
+ aggregation_bandwidth * aggregation_duration;
+
+ const int num_acks = aggregation_bytes / bytes_per_ack;
+ ASSERT_EQ(aggregation_bytes, num_acks * bytes_per_ack)
+ << "aggregation_bytes: " << aggregation_bytes << " ["
+ << aggregation_bandwidth << " in " << aggregation_duration
+ << "], bytes_per_ack: " << bytes_per_ack;
+
+ const QuicTime::Delta time_between_acks = QuicTime::Delta::FromMicroseconds(
+ aggregation_duration.ToMicroseconds() / num_acks);
+ ASSERT_EQ(aggregation_duration, num_acks * time_between_acks)
+ << "aggregation_bytes: " << aggregation_bytes
+ << ", num_acks: " << num_acks
+ << ", time_between_acks: " << time_between_acks;
+
+ // The total duration of aggregation time and quiet period.
+ const QuicTime::Delta total_duration = QuicTime::Delta::FromMicroseconds(
+ aggregation_bytes * 8 * 1000000 / bandwidth_.ToBitsPerSecond());
+ ASSERT_EQ(aggregation_bytes, total_duration * bandwidth_)
+ << "total_duration: " << total_duration
+ << ", bandwidth_: " << bandwidth_;
+
+ QuicByteCount last_extra_acked = 0;
+ for (QuicByteCount bytes = 0; bytes < aggregation_bytes;
+ bytes += bytes_per_ack) {
+ QuicByteCount extra_acked =
+ tracker_.Update(bandwidth_, RoundTripCount(), now_, bytes_per_ack);
+ QUIC_VLOG(1) << "T" << now_ << ": Update after " << bytes_per_ack
+ << " bytes acked, " << extra_acked << " extra bytes acked";
+ // |extra_acked| should be 0 if either
+ // [1] We are at the beginning of a aggregation epoch(bytes==0) and the
+ // the current tracker implementation can identify it, or
+ // [2] We are not really aggregating acks.
+ if ((bytes == 0 && expect_new_aggregation_epoch) || // [1]
+ (aggregation_bandwidth == bandwidth_)) { // [2]
+ EXPECT_EQ(0u, extra_acked);
+ } else {
+ EXPECT_LT(last_extra_acked, extra_acked);
+ }
+ now_ = now_ + time_between_acks;
+ last_extra_acked = extra_acked;
+ }
+
+ // Advance past the quiet period.
+ const QuicTime time_after_aggregation = now_;
+ now_ = start_time + total_duration;
+ QUIC_VLOG(1) << "Advanced time from " << time_after_aggregation << " to "
+ << now_ << ". Aggregation time["
+ << (time_after_aggregation - start_time) << "], Quiet time["
+ << (now_ - time_after_aggregation) << "].";
+ }
+
+ QuicRoundTripCount RoundTripCount() const {
+ return (now_ - QuicTime::Zero()).ToMicroseconds() / rtt_.ToMicroseconds();
+ }
+
+ MaxAckHeightTracker tracker_;
+ QuicBandwidth bandwidth_ = QuicBandwidth::FromBytesPerSecond(10 * 1000);
+ QuicTime now_ = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(1);
+ QuicTime::Delta rtt_ = QuicTime::Delta::FromMilliseconds(60);
+};
+
+TEST_F(MaxAckHeightTrackerTest, VeryAggregatedLargeAck) {
+ AggregationEpisode(bandwidth_ * 20, QuicTime::Delta::FromMilliseconds(6),
+ 1200, true);
+ AggregationEpisode(bandwidth_ * 20, QuicTime::Delta::FromMilliseconds(6),
+ 1200, true);
+ now_ = now_ - QuicTime::Delta::FromMilliseconds(1);
+
+ AggregationEpisode(bandwidth_ * 20, QuicTime::Delta::FromMilliseconds(6),
+ 1200, false);
+}
+
+TEST_F(MaxAckHeightTrackerTest, VeryAggregatedSmallAcks) {
+ AggregationEpisode(bandwidth_ * 20, QuicTime::Delta::FromMilliseconds(6), 300,
+ true);
+ AggregationEpisode(bandwidth_ * 20, QuicTime::Delta::FromMilliseconds(6), 300,
+ true);
+ now_ = now_ - QuicTime::Delta::FromMilliseconds(1);
+
+ AggregationEpisode(bandwidth_ * 20, QuicTime::Delta::FromMilliseconds(6), 300,
+ false);
+}
+
+TEST_F(MaxAckHeightTrackerTest, SomewhatAggregatedLargeAck) {
+ AggregationEpisode(bandwidth_ * 2, QuicTime::Delta::FromMilliseconds(50),
+ 1000, true);
+ AggregationEpisode(bandwidth_ * 2, QuicTime::Delta::FromMilliseconds(50),
+ 1000, true);
+ now_ = now_ - QuicTime::Delta::FromMilliseconds(1);
+
+ AggregationEpisode(bandwidth_ * 2, QuicTime::Delta::FromMilliseconds(50),
+ 1000, false);
+}
+
+TEST_F(MaxAckHeightTrackerTest, SomewhatAggregatedSmallAcks) {
+ AggregationEpisode(bandwidth_ * 2, QuicTime::Delta::FromMilliseconds(50), 100,
+ true);
+ AggregationEpisode(bandwidth_ * 2, QuicTime::Delta::FromMilliseconds(50), 100,
+ true);
+ now_ = now_ - QuicTime::Delta::FromMilliseconds(1);
+
+ AggregationEpisode(bandwidth_ * 2, QuicTime::Delta::FromMilliseconds(50), 100,
+ false);
+}
+
+TEST_F(MaxAckHeightTrackerTest, NotAggregated) {
+ AggregationEpisode(bandwidth_, QuicTime::Delta::FromMilliseconds(100), 100,
+ true);
+}
+
} // namespace test
} // namespace quic