In quic, clear last_inflight_packets_sent_time_ of a packet number space if there is no bytes in flight. protected by gfe2_reloadable_flag_quic_fix_last_inflight_packets_sent_time.

PiperOrigin-RevId: 312363743
Change-Id: Ie497ea4cd11d68595993f7597b039d4101d2f011
diff --git a/quic/core/quic_sent_packet_manager_test.cc b/quic/core/quic_sent_packet_manager_test.cc
index d479c68..7125986 100644
--- a/quic/core/quic_sent_packet_manager_test.cc
+++ b/quic/core/quic_sent_packet_manager_test.cc
@@ -4003,6 +4003,68 @@
   manager_.MaybeSendProbePackets();
 }
 
+// Regression test for b/156487311
+TEST_F(QuicSentPacketManagerTest, ClearLastInflightPacketsSentTime) {
+  manager_.EnableMultiplePacketNumberSpacesSupport();
+  EXPECT_CALL(*send_algorithm_, PacingRate(_))
+      .WillRepeatedly(Return(QuicBandwidth::Zero()));
+  EXPECT_CALL(*send_algorithm_, GetCongestionWindow())
+      .WillRepeatedly(Return(10 * kDefaultTCPMSS));
+
+  // Send INITIAL 1.
+  SendDataPacket(1, ENCRYPTION_INITIAL);
+  const QuicTime packet1_sent_time = clock_.Now();
+  // Send HANDSHAKE 2.
+  clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
+  SendDataPacket(2, ENCRYPTION_HANDSHAKE);
+  SendDataPacket(3, ENCRYPTION_HANDSHAKE);
+  SendDataPacket(4, ENCRYPTION_HANDSHAKE);
+  const QuicTime packet2_sent_time = clock_.Now();
+
+  // Send half RTT 5.
+  clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
+  SendDataPacket(5, ENCRYPTION_FORWARD_SECURE);
+
+  // Received ACK for INITIAL 1.
+  ExpectAck(1);
+  clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(90));
+  manager_.OnAckFrameStart(QuicPacketNumber(1), QuicTime::Delta::Infinite(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
+  EXPECT_EQ(PACKETS_NEWLY_ACKED,
+            manager_.OnAckFrameEnd(clock_.Now(), QuicPacketNumber(1),
+                                   ENCRYPTION_INITIAL));
+  RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats());
+  int pto_rttvar_multiplier =
+      GetQuicReloadableFlag(quic_default_on_pto) ? 2 : 4;
+  const QuicTime::Delta pto_delay =
+      rtt_stats->smoothed_rtt() +
+      pto_rttvar_multiplier * rtt_stats->mean_deviation() +
+      QuicTime::Delta::FromMilliseconds(kDefaultDelayedAckTimeMs);
+  if (GetQuicReloadableFlag(quic_fix_last_inflight_packets_sent_time)) {
+    // Verify PTO is armed based on handshake data.
+    EXPECT_EQ(packet2_sent_time + pto_delay, manager_.GetRetransmissionTime());
+  } else {
+    // Problematic: PTO is still armed based on initial data.
+    EXPECT_EQ(packet1_sent_time + pto_delay, manager_.GetRetransmissionTime());
+    clock_.AdvanceTime(pto_delay);
+    manager_.OnRetransmissionTimeout();
+    // Nothing to retransmit in INITIAL space.
+    EXPECT_CALL(notifier_, RetransmitFrames(_, _)).Times(0);
+    manager_.MaybeSendProbePackets();
+    // PING packet gets sent.
+    SendPingPacket(6, ENCRYPTION_INITIAL);
+
+    // Verify PTO is armed based on packet 2.
+    EXPECT_EQ(packet2_sent_time + pto_delay * 2,
+              manager_.GetRetransmissionTime());
+    clock_.AdvanceTime(pto_delay * 2);
+    manager_.OnRetransmissionTimeout();
+    EXPECT_CALL(notifier_, RetransmitFrames(_, _)).Times(testing::AtLeast(1));
+    manager_.MaybeSendProbePackets();
+  }
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic