Internal QUICHE change

PiperOrigin-RevId: 302100818
Change-Id: Ic15954c478f38fa61d0059a737dff6560de74454
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index f73b736..cd83e94 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -452,6 +452,9 @@
       SetWriteBlocked();
       return WriteResult(WRITE_STATUS_BLOCKED, /*errno*/ -1);
     }
+    if (write_should_fail_) {
+      return WriteResult(WRITE_STATUS_ERROR, /*errno*/ -1);
+    }
     int bytes_flushed = bytes_buffered_;
     bytes_buffered_ = 0;
     return WriteResult(WRITE_STATUS_OK, bytes_flushed);
@@ -5142,6 +5145,79 @@
   }
 }
 
+// After a successful MTU probe, one and only one write error should be ignored
+// if it happened in QuicConnection::FlushPacket.
+TEST_P(QuicConnectionTest,
+       MtuDiscoveryIgnoreOneWriteErrorInFlushAfterSuccessfulProbes) {
+  MtuDiscoveryTestInit();
+  writer_->SetBatchMode(true);
+
+  const QuicPacketCount packets_between_probes_base = 5;
+  set_packets_between_probes_base(packets_between_probes_base);
+
+  connection_.EnablePathMtuDiscovery(send_algorithm_);
+
+  const QuicByteCount original_max_packet_length =
+      connection_.max_packet_length();
+  // Send enough packets so that the next one triggers path MTU discovery.
+  for (QuicPacketCount i = 0; i < packets_between_probes_base - 1; i++) {
+    SendStreamDataToPeer(3, ".", i, NO_FIN, nullptr);
+    ASSERT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet());
+  }
+
+  // Trigger the probe.
+  SendStreamDataToPeer(3, "!", packets_between_probes_base - 1, NO_FIN,
+                       nullptr);
+  ASSERT_TRUE(connection_.GetMtuDiscoveryAlarm()->IsSet());
+  QuicByteCount probe_size;
+  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+      .WillOnce(SaveArg<3>(&probe_size));
+  connection_.GetMtuDiscoveryAlarm()->Fire();
+
+  EXPECT_THAT(probe_size, InRange(connection_.max_packet_length(),
+                                  kMtuDiscoveryTargetPacketSizeHigh));
+
+  const QuicPacketNumber probe_packet_number =
+      FirstSendingPacketNumber() + packets_between_probes_base;
+  ASSERT_EQ(probe_packet_number, creator_->packet_number());
+
+  // Acknowledge all packets sent so far.
+  QuicAckFrame probe_ack = InitAckFrame(probe_packet_number);
+  EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _))
+      .Times(AnyNumber());
+  ProcessAckPacket(&probe_ack);
+  EXPECT_EQ(probe_size, connection_.max_packet_length());
+  EXPECT_EQ(0u, connection_.GetBytesInFlight());
+
+  EXPECT_EQ(1u, connection_.mtu_probe_count());
+
+  if (GetQuicReloadableFlag(quic_ignore_one_write_error_after_mtu_probe)) {
+    writer_->SetShouldWriteFail();
+
+    // Ignore PACKET_WRITE_ERROR once.
+    {
+      QuicConnection::ScopedPacketFlusher flusher(&connection_);
+      // flusher's destructor will call connection_.FlushPackets, which should
+      // get a WRITE_STATUS_ERROR from the writer and ignore it.
+    }
+    EXPECT_EQ(original_max_packet_length, connection_.max_packet_length());
+    EXPECT_TRUE(connection_.connected());
+
+    // Close connection on another PACKET_WRITE_ERROR.
+    EXPECT_CALL(visitor_, OnConnectionClosed(_, _))
+        .WillOnce(Invoke(this, &QuicConnectionTest::SaveConnectionCloseFrame));
+    {
+      QuicConnection::ScopedPacketFlusher flusher(&connection_);
+      // flusher's destructor will call connection_.FlushPackets, which should
+      // get a WRITE_STATUS_ERROR from the writer and ignore it.
+    }
+    EXPECT_EQ(original_max_packet_length, connection_.max_packet_length());
+    EXPECT_FALSE(connection_.connected());
+    EXPECT_THAT(saved_connection_close_frame_.quic_error_code,
+                IsError(QUIC_PACKET_WRITE_ERROR));
+  }
+}
+
 // Simulate the case where the first attempt to send a probe is write blocked,
 // and after unblock, the second attempt returns a MSG_TOO_BIG error.
 TEST_P(QuicConnectionTest, MtuDiscoveryWriteBlocked) {