gfe-relnote: In QUIC, call NeuterHandshakePackets() at most once per connection. Protected by gfe2_reloadable_flag_quic_neuter_handshake_packets_once2 which replaces gfe2_reloadable_flag_quic_neuter_handshake_packets_once.

Also fix the bug where RTO and PTO try to retransmit not in flight packets.

PiperOrigin-RevId: 279118870
Change-Id: I1235372277109c18fded6c8d162a2147424d3dbd
diff --git a/quic/core/quic_sent_packet_manager_test.cc b/quic/core/quic_sent_packet_manager_test.cc
index 539ce97..1bd08d9 100644
--- a/quic/core/quic_sent_packet_manager_test.cc
+++ b/quic/core/quic_sent_packet_manager_test.cc
@@ -1125,9 +1125,9 @@
 
   // Now neuter all unacked unencrypted packets, which occurs when the
   // connection goes forward secure.
-  manager_.NeuterUnencryptedPackets();
   EXPECT_CALL(notifier_, HasUnackedCryptoData()).WillRepeatedly(Return(false));
   EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false));
+  manager_.NeuterUnencryptedPackets();
   EXPECT_FALSE(manager_.HasUnackedCryptoPackets());
   uint64_t unacked[] = {1, 2, 3};
   VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
@@ -3055,6 +3055,58 @@
             manager_.GetRetransmissionTime());
 }
 
+// Regression test for b/143962153
+TEST_F(QuicSentPacketManagerTest, RtoNotInFlightPacket) {
+  QuicSentPacketManagerPeer::SetMaxTailLossProbes(&manager_, 2);
+  // Send SHLO.
+  QuicStreamFrame crypto_frame(1, false, 0, QuicStringPiece());
+  SendCryptoPacket(1);
+  // Send data packet.
+  SendDataPacket(2, ENCRYPTION_FORWARD_SECURE);
+
+  // Successfully decrypt a forward secure packet.
+  if (GetQuicReloadableFlag(quic_neuter_handshake_packets_once2)) {
+    EXPECT_CALL(notifier_, OnFrameAcked(_, _, _)).Times(1);
+  } else {
+    EXPECT_CALL(notifier_, OnFrameAcked(_, _, _)).Times(0);
+  }
+  manager_.SetHandshakeConfirmed();
+
+  // 1st TLP.
+  manager_.OnRetransmissionTimeout();
+  EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+      .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) {
+        RetransmitDataPacket(3, type, ENCRYPTION_FORWARD_SECURE);
+      })));
+  manager_.MaybeRetransmitTailLossProbe();
+
+  // 2nd TLP.
+  manager_.OnRetransmissionTimeout();
+  EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+      .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) {
+        RetransmitDataPacket(4, type, ENCRYPTION_FORWARD_SECURE);
+      })));
+  manager_.MaybeRetransmitTailLossProbe();
+
+  // RTO retransmits SHLO although it is not in flight.
+  size_t num_rto_packets = 2;
+  if (GetQuicReloadableFlag(quic_neuter_handshake_packets_once2)) {
+    num_rto_packets = 1;
+  }
+  EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+      .Times(num_rto_packets)
+      .WillOnce(
+          WithArgs<0>(Invoke([this, &crypto_frame](const QuicFrames& frames) {
+            EXPECT_EQ(1u, frames.size());
+            if (GetQuicReloadableFlag(quic_neuter_handshake_packets_once2)) {
+              EXPECT_NE(crypto_frame, frames[0].stream_frame);
+            } else {
+              EXPECT_EQ(crypto_frame, frames[0].stream_frame);
+            }
+          })));
+  manager_.OnRetransmissionTimeout();
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic