Project import generated by Copybara.

PiperOrigin-RevId: 237361882
Change-Id: I109a68f44db867b20f8c6a7732b0ce657133e52a
diff --git a/quic/test_tools/packet_dropping_test_writer.cc b/quic/test_tools/packet_dropping_test_writer.cc
new file mode 100644
index 0000000..87935d5
--- /dev/null
+++ b/quic/test_tools/packet_dropping_test_writer.cc
@@ -0,0 +1,252 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/packet_dropping_test_writer.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/quic/platform/impl/quic_socket_utils.h"
+
+namespace quic {
+namespace test {
+
+const int32_t kMaxConsecutivePacketLoss = 3;
+
+// An alarm that is scheduled if a blocked socket is simulated to indicate
+// it's writable again.
+class WriteUnblockedAlarm : public QuicAlarm::Delegate {
+ public:
+  explicit WriteUnblockedAlarm(PacketDroppingTestWriter* writer)
+      : writer_(writer) {}
+
+  void OnAlarm() override {
+    QUIC_DLOG(INFO) << "Unblocking socket.";
+    writer_->OnCanWrite();
+  }
+
+ private:
+  PacketDroppingTestWriter* writer_;
+};
+
+// An alarm that is scheduled every time a new packet is to be written at a
+// later point.
+class DelayAlarm : public QuicAlarm::Delegate {
+ public:
+  explicit DelayAlarm(PacketDroppingTestWriter* writer) : writer_(writer) {}
+
+  void OnAlarm() override {
+    QuicTime new_deadline = writer_->ReleaseOldPackets();
+    if (new_deadline.IsInitialized()) {
+      writer_->SetDelayAlarm(new_deadline);
+    }
+  }
+
+ private:
+  PacketDroppingTestWriter* writer_;
+};
+
+PacketDroppingTestWriter::PacketDroppingTestWriter()
+    : clock_(nullptr),
+      cur_buffer_size_(0),
+      num_calls_to_write_(0),
+      fake_packet_loss_percentage_(0),
+      fake_drop_first_n_packets_(0),
+      fake_blocked_socket_percentage_(0),
+      fake_packet_reorder_percentage_(0),
+      fake_packet_delay_(QuicTime::Delta::Zero()),
+      fake_bandwidth_(QuicBandwidth::Zero()),
+      buffer_size_(0),
+      num_consecutive_packet_lost_(0) {
+  uint64_t seed = QuicRandom::GetInstance()->RandUint64();
+  QUIC_LOG(INFO) << "Seeding packet loss with " << seed;
+  simple_random_.set_seed(seed);
+}
+
+PacketDroppingTestWriter::~PacketDroppingTestWriter() = default;
+
+void PacketDroppingTestWriter::Initialize(
+    QuicConnectionHelperInterface* helper,
+    QuicAlarmFactory* alarm_factory,
+    std::unique_ptr<Delegate> on_can_write) {
+  clock_ = helper->GetClock();
+  write_unblocked_alarm_.reset(
+      alarm_factory->CreateAlarm(new WriteUnblockedAlarm(this)));
+  delay_alarm_.reset(alarm_factory->CreateAlarm(new DelayAlarm(this)));
+  on_can_write_ = std::move(on_can_write);
+}
+
+WriteResult PacketDroppingTestWriter::WritePacket(
+    const char* buffer,
+    size_t buf_len,
+    const QuicIpAddress& self_address,
+    const QuicSocketAddress& peer_address,
+    PerPacketOptions* options) {
+  ++num_calls_to_write_;
+  ReleaseOldPackets();
+
+  QuicWriterMutexLock lock(&config_mutex_);
+  if (fake_drop_first_n_packets_ > 0 &&
+      num_calls_to_write_ <=
+          static_cast<uint64_t>(fake_drop_first_n_packets_)) {
+    QUIC_DVLOG(1) << "Dropping first " << fake_drop_first_n_packets_
+                  << " packets (packet number " << num_calls_to_write_ << ")";
+    return WriteResult(WRITE_STATUS_OK, buf_len);
+  }
+  const int32_t kMaxPacketLossPercentage =
+      kMaxConsecutivePacketLoss * 100.0 / (kMaxConsecutivePacketLoss + 1);
+  if (fake_packet_loss_percentage_ > 0 &&
+      // Do not allow too many consecutive packet drops to avoid test flakiness.
+      (num_consecutive_packet_lost_ <= kMaxConsecutivePacketLoss ||
+       // Allow as many consecutive packet drops as possbile if
+       // |fake_packet_lost_percentage_| is large enough. Without this exception
+       // it is hard to simulate high loss rate, like 100%.
+       fake_packet_loss_percentage_ > kMaxPacketLossPercentage) &&
+      (simple_random_.RandUint64() % 100 <
+       static_cast<uint64_t>(fake_packet_loss_percentage_))) {
+    QUIC_DVLOG(1) << "Dropping packet.";
+    ++num_consecutive_packet_lost_;
+    return WriteResult(WRITE_STATUS_OK, buf_len);
+  } else {
+    num_consecutive_packet_lost_ = 0;
+  }
+  if (fake_blocked_socket_percentage_ > 0 &&
+      simple_random_.RandUint64() % 100 <
+          static_cast<uint64_t>(fake_blocked_socket_percentage_)) {
+    CHECK(on_can_write_ != nullptr);
+    QUIC_DVLOG(1) << "Blocking socket.";
+    if (!write_unblocked_alarm_->IsSet()) {
+      // Set the alarm to fire immediately.
+      write_unblocked_alarm_->Set(clock_->ApproximateNow());
+    }
+    return WriteResult(WRITE_STATUS_BLOCKED, EAGAIN);
+  }
+
+  if (!fake_packet_delay_.IsZero() || !fake_bandwidth_.IsZero()) {
+    if (buffer_size_ > 0 && buf_len + cur_buffer_size_ > buffer_size_) {
+      // Drop packets which do not fit into the buffer.
+      QUIC_DVLOG(1) << "Dropping packet because the buffer is full.";
+      return WriteResult(WRITE_STATUS_OK, buf_len);
+    }
+
+    // Queue it to be sent.
+    QuicTime send_time = clock_->ApproximateNow() + fake_packet_delay_;
+    if (!fake_bandwidth_.IsZero()) {
+      // Calculate a time the bandwidth limit would impose.
+      QuicTime::Delta bandwidth_delay = QuicTime::Delta::FromMicroseconds(
+          (buf_len * kNumMicrosPerSecond) / fake_bandwidth_.ToBytesPerSecond());
+      send_time = delayed_packets_.empty()
+                      ? send_time + bandwidth_delay
+                      : delayed_packets_.back().send_time + bandwidth_delay;
+    }
+    std::unique_ptr<PerPacketOptions> delayed_options;
+    if (options != nullptr) {
+      delayed_options = options->Clone();
+    }
+    delayed_packets_.push_back(
+        DelayedWrite(buffer, buf_len, self_address, peer_address,
+                     std::move(delayed_options), send_time));
+    cur_buffer_size_ += buf_len;
+
+    // Set the alarm if it's not yet set.
+    if (!delay_alarm_->IsSet()) {
+      delay_alarm_->Set(send_time);
+    }
+
+    return WriteResult(WRITE_STATUS_OK, buf_len);
+  }
+
+  return QuicPacketWriterWrapper::WritePacket(buffer, buf_len, self_address,
+                                              peer_address, options);
+}
+
+bool PacketDroppingTestWriter::IsWriteBlocked() const {
+  if (write_unblocked_alarm_ != nullptr && write_unblocked_alarm_->IsSet()) {
+    return true;
+  }
+  return QuicPacketWriterWrapper::IsWriteBlocked();
+}
+
+void PacketDroppingTestWriter::SetWritable() {
+  if (write_unblocked_alarm_ != nullptr && write_unblocked_alarm_->IsSet()) {
+    write_unblocked_alarm_->Cancel();
+  }
+  QuicPacketWriterWrapper::SetWritable();
+}
+
+QuicTime PacketDroppingTestWriter::ReleaseNextPacket() {
+  if (delayed_packets_.empty()) {
+    return QuicTime::Zero();
+  }
+  QuicReaderMutexLock lock(&config_mutex_);
+  auto iter = delayed_packets_.begin();
+  // Determine if we should re-order.
+  if (delayed_packets_.size() > 1 && fake_packet_reorder_percentage_ > 0 &&
+      simple_random_.RandUint64() % 100 <
+          static_cast<uint64_t>(fake_packet_reorder_percentage_)) {
+    QUIC_DLOG(INFO) << "Reordering packets.";
+    ++iter;
+    // Swap the send times when re-ordering packets.
+    delayed_packets_.begin()->send_time = iter->send_time;
+  }
+
+  QUIC_DVLOG(1) << "Releasing packet.  " << (delayed_packets_.size() - 1)
+                << " remaining.";
+  // Grab the next one off the queue and send it.
+  QuicPacketWriterWrapper::WritePacket(
+      iter->buffer.data(), iter->buffer.length(), iter->self_address,
+      iter->peer_address, iter->options.get());
+  DCHECK_GE(cur_buffer_size_, iter->buffer.length());
+  cur_buffer_size_ -= iter->buffer.length();
+  delayed_packets_.erase(iter);
+
+  // If there are others, find the time for the next to be sent.
+  if (delayed_packets_.empty()) {
+    return QuicTime::Zero();
+  }
+  return delayed_packets_.begin()->send_time;
+}
+
+QuicTime PacketDroppingTestWriter::ReleaseOldPackets() {
+  while (!delayed_packets_.empty()) {
+    QuicTime next_send_time = delayed_packets_.front().send_time;
+    if (next_send_time > clock_->Now()) {
+      return next_send_time;
+    }
+    ReleaseNextPacket();
+  }
+  return QuicTime::Zero();
+}
+
+void PacketDroppingTestWriter::SetDelayAlarm(QuicTime new_deadline) {
+  delay_alarm_->Set(new_deadline);
+}
+
+void PacketDroppingTestWriter::OnCanWrite() {
+  on_can_write_->OnCanWrite();
+}
+
+void PacketDroppingTestWriter::set_fake_packet_loss_percentage(
+    int32_t fake_packet_loss_percentage) {
+  QuicWriterMutexLock lock(&config_mutex_);
+  fake_packet_loss_percentage_ = fake_packet_loss_percentage;
+  num_consecutive_packet_lost_ = 0;
+}
+
+PacketDroppingTestWriter::DelayedWrite::DelayedWrite(
+    const char* buffer,
+    size_t buf_len,
+    const QuicIpAddress& self_address,
+    const QuicSocketAddress& peer_address,
+    std::unique_ptr<PerPacketOptions> options,
+    QuicTime send_time)
+    : buffer(buffer, buf_len),
+      self_address(self_address),
+      peer_address(peer_address),
+      options(std::move(options)),
+      send_time(send_time) {}
+
+PacketDroppingTestWriter::DelayedWrite::~DelayedWrite() = default;
+
+}  // namespace test
+}  // namespace quic