// Copyright 2016 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 "quiche/quic/core/congestion_control/bbr_sender.h"

#include <algorithm>
#include <map>
#include <memory>
#include <utility>

#include "quiche/quic/core/congestion_control/rtt_stats.h"
#include "quiche/quic/core/crypto/crypto_protocol.h"
#include "quiche/quic/core/quic_bandwidth.h"
#include "quiche/quic/core/quic_packets.h"
#include "quiche/quic/core/quic_types.h"
#include "quiche/quic/core/quic_utils.h"
#include "quiche/quic/platform/api/quic_flags.h"
#include "quiche/quic/platform/api/quic_logging.h"
#include "quiche/quic/platform/api/quic_test.h"
#include "quiche/quic/test_tools/mock_clock.h"
#include "quiche/quic/test_tools/quic_config_peer.h"
#include "quiche/quic/test_tools/quic_connection_peer.h"
#include "quiche/quic/test_tools/quic_sent_packet_manager_peer.h"
#include "quiche/quic/test_tools/quic_test_utils.h"
#include "quiche/quic/test_tools/send_algorithm_test_result.pb.h"
#include "quiche/quic/test_tools/send_algorithm_test_utils.h"
#include "quiche/quic/test_tools/simulator/quic_endpoint.h"
#include "quiche/quic/test_tools/simulator/simulator.h"
#include "quiche/quic/test_tools/simulator/switch.h"
#include "quiche/common/platform/api/quiche_command_line_flags.h"

using testing::AllOf;
using testing::Ge;
using testing::Le;

DEFINE_QUICHE_COMMAND_LINE_FLAG(
    std::string, quic_bbr_test_regression_mode, "",
    "One of a) 'record' to record test result (one file per test), or "
    "b) 'regress' to regress against recorded results, or "
    "c) <anything else> for non-regression mode.");

namespace quic {
namespace test {

// Use the initial CWND of 10, as 32 is too much for the test network.
const uint32_t kInitialCongestionWindowPackets = 10;
const uint32_t kDefaultWindowTCP =
    kInitialCongestionWindowPackets * kDefaultTCPMSS;

// Test network parameters.  Here, the topology of the network is:
//
//          BBR sender
//               |
//               |  <-- local link (10 Mbps, 2 ms delay)
//               |
//        Network switch
//               *  <-- the bottleneck queue in the direction
//               |          of the receiver
//               |
//               |  <-- test link (4 Mbps, 30 ms delay)
//               |
//               |
//           Receiver
//
// The reason the bandwidths chosen are relatively low is the fact that the
// connection simulator uses QuicTime for its internal clock, and as such has
// the granularity of 1us, meaning that at bandwidth higher than 20 Mbps the
// packets can start to land on the same timestamp.
const QuicBandwidth kTestLinkBandwidth =
    QuicBandwidth::FromKBitsPerSecond(4000);
const QuicBandwidth kLocalLinkBandwidth =
    QuicBandwidth::FromKBitsPerSecond(10000);
const QuicTime::Delta kTestPropagationDelay =
    QuicTime::Delta::FromMilliseconds(30);
const QuicTime::Delta kLocalPropagationDelay =
    QuicTime::Delta::FromMilliseconds(2);
const QuicTime::Delta kTestTransferTime =
    kTestLinkBandwidth.TransferTime(kMaxOutgoingPacketSize) +
    kLocalLinkBandwidth.TransferTime(kMaxOutgoingPacketSize);
const QuicTime::Delta kTestRtt =
    (kTestPropagationDelay + kLocalPropagationDelay + kTestTransferTime) * 2;
const QuicByteCount kTestBdp = kTestRtt * kTestLinkBandwidth;

class BbrSenderTest : public QuicTest {
 protected:
  BbrSenderTest()
      : simulator_(&random_),
        bbr_sender_(&simulator_, "BBR sender", "Receiver",
                    Perspective::IS_CLIENT,
                    /*connection_id=*/TestConnectionId(42)),
        competing_sender_(&simulator_, "Competing sender", "Competing receiver",
                          Perspective::IS_CLIENT,
                          /*connection_id=*/TestConnectionId(43)),
        receiver_(&simulator_, "Receiver", "BBR sender", Perspective::IS_SERVER,
                  /*connection_id=*/TestConnectionId(42)),
        competing_receiver_(&simulator_, "Competing receiver",
                            "Competing sender", Perspective::IS_SERVER,
                            /*connection_id=*/TestConnectionId(43)),
        receiver_multiplexer_("Receiver multiplexer",
                              {&receiver_, &competing_receiver_}) {
    rtt_stats_ = bbr_sender_.connection()->sent_packet_manager().GetRttStats();
    const int kTestMaxPacketSize = 1350;
    bbr_sender_.connection()->SetMaxPacketLength(kTestMaxPacketSize);
    sender_ = SetupBbrSender(&bbr_sender_);
    SetConnectionOption(kBBRA);
    clock_ = simulator_.GetClock();
  }

  void SetUp() override {
    if (quiche::GetQuicheCommandLineFlag(FLAGS_quic_bbr_test_regression_mode) ==
        "regress") {
      SendAlgorithmTestResult expected;
      ASSERT_TRUE(LoadSendAlgorithmTestResult(&expected));
      random_seed_ = expected.random_seed();
    } else {
      random_seed_ = QuicRandom::GetInstance()->RandUint64();
    }
    random_.set_seed(random_seed_);
    QUIC_LOG(INFO) << "BbrSenderTest simulator set up.  Seed: " << random_seed_;
  }

  ~BbrSenderTest() {
    const std::string regression_mode =
        quiche::GetQuicheCommandLineFlag(FLAGS_quic_bbr_test_regression_mode);
    const QuicTime::Delta simulated_duration = clock_->Now() - QuicTime::Zero();
    if (regression_mode == "record") {
      RecordSendAlgorithmTestResult(random_seed_,
                                    simulated_duration.ToMicroseconds());
    } else if (regression_mode == "regress") {
      CompareSendAlgorithmTestResult(simulated_duration.ToMicroseconds());
    }
  }

  uint64_t random_seed_;
  SimpleRandom random_;
  simulator::Simulator simulator_;
  simulator::QuicEndpoint bbr_sender_;
  simulator::QuicEndpoint competing_sender_;
  simulator::QuicEndpoint receiver_;
  simulator::QuicEndpoint competing_receiver_;
  simulator::QuicEndpointMultiplexer receiver_multiplexer_;
  std::unique_ptr<simulator::Switch> switch_;
  std::unique_ptr<simulator::SymmetricLink> bbr_sender_link_;
  std::unique_ptr<simulator::SymmetricLink> competing_sender_link_;
  std::unique_ptr<simulator::SymmetricLink> receiver_link_;

  // Owned by different components of the connection.
  const QuicClock* clock_;
  const RttStats* rtt_stats_;
  BbrSender* sender_;

  // Enables BBR on |endpoint| and returns the associated BBR congestion
  // controller.
  BbrSender* SetupBbrSender(simulator::QuicEndpoint* endpoint) {
    const RttStats* rtt_stats =
        endpoint->connection()->sent_packet_manager().GetRttStats();
    // Ownership of the sender will be overtaken by the endpoint.
    BbrSender* sender = new BbrSender(
        endpoint->connection()->clock()->Now(), rtt_stats,
        QuicSentPacketManagerPeer::GetUnackedPacketMap(
            QuicConnectionPeer::GetSentPacketManager(endpoint->connection())),
        kInitialCongestionWindowPackets,
        GetQuicFlag(quic_max_congestion_window), &random_,
        QuicConnectionPeer::GetStats(endpoint->connection()));
    QuicConnectionPeer::SetSendAlgorithm(endpoint->connection(), sender);
    endpoint->RecordTrace();
    return sender;
  }

  // Creates a default setup, which is a network with a bottleneck between the
  // receiver and the switch.  The switch has the buffers four times larger than
  // the bottleneck BDP, which should guarantee a lack of losses.
  void CreateDefaultSetup() {
    switch_ = std::make_unique<simulator::Switch>(&simulator_, "Switch", 8,
                                                  2 * kTestBdp);
    bbr_sender_link_ = std::make_unique<simulator::SymmetricLink>(
        &bbr_sender_, switch_->port(1), kLocalLinkBandwidth,
        kLocalPropagationDelay);
    receiver_link_ = std::make_unique<simulator::SymmetricLink>(
        &receiver_, switch_->port(2), kTestLinkBandwidth,
        kTestPropagationDelay);
  }

  // Same as the default setup, except the buffer now is half of the BDP.
  void CreateSmallBufferSetup() {
    switch_ = std::make_unique<simulator::Switch>(&simulator_, "Switch", 8,
                                                  0.5 * kTestBdp);
    bbr_sender_link_ = std::make_unique<simulator::SymmetricLink>(
        &bbr_sender_, switch_->port(1), kLocalLinkBandwidth,
        kLocalPropagationDelay);
    receiver_link_ = std::make_unique<simulator::SymmetricLink>(
        &receiver_, switch_->port(2), kTestLinkBandwidth,
        kTestPropagationDelay);
  }

  // Creates the variation of the default setup in which there is another sender
  // that competes for the same bottleneck link.
  void CreateCompetitionSetup() {
    switch_ = std::make_unique<simulator::Switch>(&simulator_, "Switch", 8,
                                                  2 * kTestBdp);

    // Add a small offset to the competing link in order to avoid
    // synchronization effects.
    const QuicTime::Delta small_offset = QuicTime::Delta::FromMicroseconds(3);
    bbr_sender_link_ = std::make_unique<simulator::SymmetricLink>(
        &bbr_sender_, switch_->port(1), kLocalLinkBandwidth,
        kLocalPropagationDelay);
    competing_sender_link_ = std::make_unique<simulator::SymmetricLink>(
        &competing_sender_, switch_->port(3), kLocalLinkBandwidth,
        kLocalPropagationDelay + small_offset);
    receiver_link_ = std::make_unique<simulator::SymmetricLink>(
        &receiver_multiplexer_, switch_->port(2), kTestLinkBandwidth,
        kTestPropagationDelay);
  }

  // Creates a BBR vs BBR competition setup.
  void CreateBbrVsBbrSetup() {
    SetupBbrSender(&competing_sender_);
    CreateCompetitionSetup();
  }

  void EnableAggregation(QuicByteCount aggregation_bytes,
                         QuicTime::Delta aggregation_timeout) {
    // Enable aggregation on the path from the receiver to the sender.
    switch_->port_queue(1)->EnableAggregation(aggregation_bytes,
                                              aggregation_timeout);
  }

  void DoSimpleTransfer(QuicByteCount transfer_size, QuicTime::Delta deadline) {
    bbr_sender_.AddBytesToTransfer(transfer_size);
    // TODO(vasilvv): consider rewriting this to run until the receiver actually
    // receives the intended amount of bytes.
    bool simulator_result = simulator_.RunUntilOrTimeout(
        [this]() { return bbr_sender_.bytes_to_transfer() == 0; }, deadline);
    EXPECT_TRUE(simulator_result)
        << "Simple transfer failed.  Bytes remaining: "
        << bbr_sender_.bytes_to_transfer();
    QUIC_LOG(INFO) << "Simple transfer state: " << sender_->ExportDebugState();
  }

  // Drive the simulator by sending enough data to enter PROBE_BW.
  void DriveOutOfStartup() {
    ASSERT_FALSE(sender_->ExportDebugState().is_at_full_bandwidth);
    DoSimpleTransfer(1024 * 1024, QuicTime::Delta::FromSeconds(15));
    EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode);
    EXPECT_APPROX_EQ(kTestLinkBandwidth,
                     sender_->ExportDebugState().max_bandwidth, 0.02f);
  }

  // Send |bytes|-sized bursts of data |number_of_bursts| times, waiting for
  // |wait_time| between each burst.
  void SendBursts(size_t number_of_bursts, QuicByteCount bytes,
                  QuicTime::Delta wait_time) {
    ASSERT_EQ(0u, bbr_sender_.bytes_to_transfer());
    for (size_t i = 0; i < number_of_bursts; i++) {
      bbr_sender_.AddBytesToTransfer(bytes);

      // Transfer data and wait for three seconds between each transfer.
      simulator_.RunFor(wait_time);

      // Ensure the connection did not time out.
      ASSERT_TRUE(bbr_sender_.connection()->connected());
      ASSERT_TRUE(receiver_.connection()->connected());
    }

    simulator_.RunFor(wait_time + kTestRtt);
    ASSERT_EQ(0u, bbr_sender_.bytes_to_transfer());
  }

  void SetConnectionOption(QuicTag option) {
    QuicConfig config;
    QuicTagVector options;
    options.push_back(option);
    QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
    sender_->SetFromConfig(config, Perspective::IS_SERVER);
  }
};

TEST_F(BbrSenderTest, SetInitialCongestionWindow) {
  EXPECT_NE(3u * kDefaultTCPMSS, sender_->GetCongestionWindow());
  sender_->SetInitialCongestionWindowInPackets(3);
  EXPECT_EQ(3u * kDefaultTCPMSS, sender_->GetCongestionWindow());
}

// Test a simple long data transfer in the default setup.
TEST_F(BbrSenderTest, SimpleTransfer) {
  CreateDefaultSetup();

  // At startup make sure we are at the default.
  EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow());
  // At startup make sure we can send.
  EXPECT_TRUE(sender_->CanSend(0));
  // And that window is un-affected.
  EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow());

  // Verify that Sender is in slow start.
  EXPECT_TRUE(sender_->InSlowStart());

  // Verify that pacing rate is based on the initial RTT.
  QuicBandwidth expected_pacing_rate = QuicBandwidth::FromBytesAndTimeDelta(
      2.885 * kDefaultWindowTCP, rtt_stats_->initial_rtt());
  EXPECT_APPROX_EQ(expected_pacing_rate.ToBitsPerSecond(),
                   sender_->PacingRate(0).ToBitsPerSecond(), 0.01f);

  ASSERT_GE(kTestBdp, kDefaultWindowTCP + kDefaultTCPMSS);

  DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(30));
  EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode);
  EXPECT_EQ(0u, bbr_sender_.connection()->GetStats().packets_lost);
  EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);

  // The margin here is quite high, since there exists a possibility that the
  // connection just exited high gain cycle.
  EXPECT_APPROX_EQ(kTestRtt, rtt_stats_->smoothed_rtt(), 0.2f);
}

TEST_F(BbrSenderTest, SimpleTransferBBRB) {
  SetConnectionOption(kBBRB);
  CreateDefaultSetup();

  // At startup make sure we are at the default.
  EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow());
  // At startup make sure we can send.
  EXPECT_TRUE(sender_->CanSend(0));
  // And that window is un-affected.
  EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow());

  // Verify that Sender is in slow start.
  EXPECT_TRUE(sender_->InSlowStart());

  // Verify that pacing rate is based on the initial RTT.
  QuicBandwidth expected_pacing_rate = QuicBandwidth::FromBytesAndTimeDelta(
      2.885 * kDefaultWindowTCP, rtt_stats_->initial_rtt());
  EXPECT_APPROX_EQ(expected_pacing_rate.ToBitsPerSecond(),
                   sender_->PacingRate(0).ToBitsPerSecond(), 0.01f);

  ASSERT_GE(kTestBdp, kDefaultWindowTCP + kDefaultTCPMSS);

  DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(30));
  EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode);
  EXPECT_EQ(0u, bbr_sender_.connection()->GetStats().packets_lost);
  EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);

  // The margin here is quite high, since there exists a possibility that the
  // connection just exited high gain cycle.
  EXPECT_APPROX_EQ(kTestRtt, rtt_stats_->smoothed_rtt(), 0.2f);
}

// Test a simple transfer in a situation when the buffer is less than BDP.
TEST_F(BbrSenderTest, SimpleTransferSmallBuffer) {
  CreateSmallBufferSetup();

  DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(30));
  EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode);
  EXPECT_APPROX_EQ(kTestLinkBandwidth,
                   sender_->ExportDebugState().max_bandwidth, 0.01f);
  EXPECT_GE(bbr_sender_.connection()->GetStats().packets_lost, 0u);
  EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);

  // The margin here is quite high, since there exists a possibility that the
  // connection just exited high gain cycle.
  EXPECT_APPROX_EQ(kTestRtt, sender_->GetMinRtt(), 0.2f);
}

TEST_F(BbrSenderTest, RemoveBytesLostInRecovery) {
  CreateDefaultSetup();

  DriveOutOfStartup();

  // Drop a packet to enter recovery.
  receiver_.DropNextIncomingPacket();
  ASSERT_TRUE(
      simulator_.RunUntilOrTimeout([this]() { return sender_->InRecovery(); },
                                   QuicTime::Delta::FromSeconds(30)));

  QuicUnackedPacketMap* unacked_packets =
      QuicSentPacketManagerPeer::GetUnackedPacketMap(
          QuicConnectionPeer::GetSentPacketManager(bbr_sender_.connection()));
  QuicPacketNumber largest_sent =
      bbr_sender_.connection()->sent_packet_manager().GetLargestSentPacket();
  // least_inflight is the smallest inflight packet.
  QuicPacketNumber least_inflight =
      bbr_sender_.connection()->sent_packet_manager().GetLeastUnacked();
  while (!unacked_packets->GetTransmissionInfo(least_inflight).in_flight) {
    ASSERT_LE(least_inflight, largest_sent);
    least_inflight++;
  }
  QuicPacketLength least_inflight_packet_size =
      unacked_packets->GetTransmissionInfo(least_inflight).bytes_sent;
  QuicByteCount prior_recovery_window =
      sender_->ExportDebugState().recovery_window;
  QuicByteCount prior_inflight = unacked_packets->bytes_in_flight();
  QUIC_LOG(INFO) << "Recovery window:" << prior_recovery_window
                 << ", least_inflight_packet_size:"
                 << least_inflight_packet_size
                 << ", bytes_in_flight:" << prior_inflight;
  ASSERT_GT(prior_recovery_window, least_inflight_packet_size);

  // Lose the least inflight packet and expect the recovery window to drop.
  unacked_packets->RemoveFromInFlight(least_inflight);
  LostPacketVector lost_packets;
  lost_packets.emplace_back(least_inflight, least_inflight_packet_size);
  sender_->OnCongestionEvent(false, prior_inflight, clock_->Now(), {},
                             lost_packets, 0, 0);
  EXPECT_EQ(sender_->ExportDebugState().recovery_window,
            prior_inflight - least_inflight_packet_size);
  EXPECT_LT(sender_->ExportDebugState().recovery_window, prior_recovery_window);
}

// Test a simple long data transfer with 2 rtts of aggregation.
TEST_F(BbrSenderTest, SimpleTransfer2RTTAggregationBytes) {
  SetConnectionOption(kBSAO);
  CreateDefaultSetup();
  // 2 RTTs of aggregation, with a max of 10kb.
  EnableAggregation(10 * 1024, 2 * kTestRtt);

  // Transfer 12MB.
  DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35));
  EXPECT_TRUE(sender_->ExportDebugState().mode == BbrSender::PROBE_BW ||
              sender_->ExportDebugState().mode == BbrSender::PROBE_RTT);

  EXPECT_APPROX_EQ(kTestLinkBandwidth,
                   sender_->ExportDebugState().max_bandwidth, 0.01f);

  // The margin here is high, because the aggregation greatly increases
  // smoothed rtt.
  EXPECT_GE(kTestRtt * 4, rtt_stats_->smoothed_rtt());
  EXPECT_APPROX_EQ(kTestRtt, rtt_stats_->min_rtt(), 0.5f);
}

// Test a simple long data transfer with 2 rtts of aggregation.
TEST_F(BbrSenderTest, SimpleTransferAckDecimation) {
  SetConnectionOption(kBSAO);
  // Decrease the CWND gain so extra CWND is required with stretch acks.
  SetQuicFlag(quic_bbr_cwnd_gain, 1.0);
  sender_ = new BbrSender(
      bbr_sender_.connection()->clock()->Now(), rtt_stats_,
      QuicSentPacketManagerPeer::GetUnackedPacketMap(
          QuicConnectionPeer::GetSentPacketManager(bbr_sender_.connection())),
      kInitialCongestionWindowPackets, GetQuicFlag(quic_max_congestion_window),
      &random_, QuicConnectionPeer::GetStats(bbr_sender_.connection()));
  QuicConnectionPeer::SetSendAlgorithm(bbr_sender_.connection(), sender_);
  CreateDefaultSetup();

  // Transfer 12MB.
  DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35));
  EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode);

  EXPECT_APPROX_EQ(kTestLinkBandwidth,
                   sender_->ExportDebugState().max_bandwidth, 0.01f);

  // TODO(ianswett): Expect 0 packets are lost once BBR no longer measures
  // bandwidth higher than the link rate.
  EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
  // The margin here is high, because the aggregation greatly increases
  // smoothed rtt.
  EXPECT_GE(kTestRtt * 2, rtt_stats_->smoothed_rtt());
  EXPECT_APPROX_EQ(kTestRtt, rtt_stats_->min_rtt(), 0.1f);
}

// Test a simple long data transfer with 2 rtts of aggregation.
// TODO(b/172302465) Re-enable this test.
TEST_F(BbrSenderTest, QUIC_TEST_DISABLED_IN_CHROME(
                          SimpleTransfer2RTTAggregationBytes20RTTWindow)) {
  SetConnectionOption(kBSAO);
  CreateDefaultSetup();
  SetConnectionOption(kBBR4);
  // 2 RTTs of aggregation, with a max of 10kb.
  EnableAggregation(10 * 1024, 2 * kTestRtt);

  // Transfer 12MB.
  DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35));
  EXPECT_TRUE(sender_->ExportDebugState().mode == BbrSender::PROBE_BW ||
              sender_->ExportDebugState().mode == BbrSender::PROBE_RTT);

  EXPECT_APPROX_EQ(kTestLinkBandwidth,
                   sender_->ExportDebugState().max_bandwidth, 0.01f);

  // TODO(ianswett): Expect 0 packets are lost once BBR no longer measures
  // bandwidth higher than the link rate.
  // The margin here is high, because the aggregation greatly increases
  // smoothed rtt.
  EXPECT_GE(kTestRtt * 4, rtt_stats_->smoothed_rtt());
  EXPECT_APPROX_EQ(kTestRtt, rtt_stats_->min_rtt(), 0.25f);
}

// Test a simple long data transfer with 2 rtts of aggregation.
TEST_F(BbrSenderTest, SimpleTransfer2RTTAggregationBytes40RTTWindow) {
  SetConnectionOption(kBSAO);
  CreateDefaultSetup();
  SetConnectionOption(kBBR5);
  // 2 RTTs of aggregation, with a max of 10kb.
  EnableAggregation(10 * 1024, 2 * kTestRtt);

  // Transfer 12MB.
  DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35));
  EXPECT_TRUE(sender_->ExportDebugState().mode == BbrSender::PROBE_BW ||
              sender_->ExportDebugState().mode == BbrSender::PROBE_RTT);

  EXPECT_APPROX_EQ(kTestLinkBandwidth,
                   sender_->ExportDebugState().max_bandwidth, 0.01f);

  // TODO(ianswett): Expect 0 packets are lost once BBR no longer measures
  // bandwidth higher than the link rate.
  // The margin here is high, because the aggregation greatly increases
  // smoothed rtt.
  EXPECT_GE(kTestRtt * 4, rtt_stats_->smoothed_rtt());
  EXPECT_APPROX_EQ(kTestRtt, rtt_stats_->min_rtt(), 0.25f);
}

// Test the number of losses incurred by the startup phase in a situation when
// the buffer is less than BDP.
TEST_F(BbrSenderTest, PacketLossOnSmallBufferStartup) {
  CreateSmallBufferSetup();

  DriveOutOfStartup();
  float loss_rate =
      static_cast<float>(bbr_sender_.connection()->GetStats().packets_lost) /
      bbr_sender_.connection()->GetStats().packets_sent;
  EXPECT_LE(loss_rate, 0.31);
}

// Test the number of losses incurred by the startup phase in a situation when
// the buffer is less than BDP, with a STARTUP CWND gain of 2.
TEST_F(BbrSenderTest, PacketLossOnSmallBufferStartupDerivedCWNDGain) {
  CreateSmallBufferSetup();

  SetConnectionOption(kBBQ2);
  DriveOutOfStartup();
  float loss_rate =
      static_cast<float>(bbr_sender_.connection()->GetStats().packets_lost) /
      bbr_sender_.connection()->GetStats().packets_sent;
  EXPECT_LE(loss_rate, 0.1);
}

// Ensures the code transitions loss recovery states correctly (NOT_IN_RECOVERY
// -> CONSERVATION -> GROWTH -> NOT_IN_RECOVERY).
TEST_F(BbrSenderTest, RecoveryStates) {
  const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(10);
  bool simulator_result;
  CreateSmallBufferSetup();

  bbr_sender_.AddBytesToTransfer(100 * 1024 * 1024);
  ASSERT_EQ(BbrSender::NOT_IN_RECOVERY,
            sender_->ExportDebugState().recovery_state);

  simulator_result = simulator_.RunUntilOrTimeout(
      [this]() {
        return sender_->ExportDebugState().recovery_state !=
               BbrSender::NOT_IN_RECOVERY;
      },
      timeout);
  ASSERT_TRUE(simulator_result);
  ASSERT_EQ(BbrSender::CONSERVATION,
            sender_->ExportDebugState().recovery_state);

  simulator_result = simulator_.RunUntilOrTimeout(
      [this]() {
        return sender_->ExportDebugState().recovery_state !=
               BbrSender::CONSERVATION;
      },
      timeout);
  ASSERT_TRUE(simulator_result);
  ASSERT_EQ(BbrSender::GROWTH, sender_->ExportDebugState().recovery_state);

  simulator_result = simulator_.RunUntilOrTimeout(
      [this]() {
        return sender_->ExportDebugState().recovery_state != BbrSender::GROWTH;
      },
      timeout);

  ASSERT_EQ(BbrSender::NOT_IN_RECOVERY,
            sender_->ExportDebugState().recovery_state);
  ASSERT_TRUE(simulator_result);
}

// Verify the behavior of the algorithm in the case when the connection sends
// small bursts of data after sending continuously for a while.
TEST_F(BbrSenderTest, ApplicationLimitedBursts) {
  CreateDefaultSetup();
  EXPECT_FALSE(sender_->HasGoodBandwidthEstimateForResumption());

  DriveOutOfStartup();
  EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
  EXPECT_TRUE(sender_->HasGoodBandwidthEstimateForResumption());

  SendBursts(20, 512, QuicTime::Delta::FromSeconds(3));
  EXPECT_TRUE(sender_->ExportDebugState().last_sample_is_app_limited);
  EXPECT_TRUE(sender_->HasGoodBandwidthEstimateForResumption());
  EXPECT_APPROX_EQ(kTestLinkBandwidth,
                   sender_->ExportDebugState().max_bandwidth, 0.01f);
}

// Verify the behavior of the algorithm in the case when the connection sends
// small bursts of data and then starts sending continuously.
TEST_F(BbrSenderTest, ApplicationLimitedBurstsWithoutPrior) {
  CreateDefaultSetup();

  SendBursts(40, 512, QuicTime::Delta::FromSeconds(3));
  EXPECT_TRUE(sender_->ExportDebugState().last_sample_is_app_limited);

  DriveOutOfStartup();
  EXPECT_APPROX_EQ(kTestLinkBandwidth,
                   sender_->ExportDebugState().max_bandwidth, 0.01f);
  EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
}

// Verify that the DRAIN phase works correctly.
TEST_F(BbrSenderTest, Drain) {
  CreateDefaultSetup();
  const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(10);
  // Get the queue at the bottleneck, which is the outgoing queue at the port to
  // which the receiver is connected.
  const simulator::Queue* queue = switch_->port_queue(2);
  bool simulator_result;

  // We have no intention of ever finishing this transfer.
  bbr_sender_.AddBytesToTransfer(100 * 1024 * 1024);

  // Run the startup, and verify that it fills up the queue.
  ASSERT_EQ(BbrSender::STARTUP, sender_->ExportDebugState().mode);
  simulator_result = simulator_.RunUntilOrTimeout(
      [this]() {
        return sender_->ExportDebugState().mode != BbrSender::STARTUP;
      },
      timeout);
  ASSERT_TRUE(simulator_result);
  ASSERT_EQ(BbrSender::DRAIN, sender_->ExportDebugState().mode);
  EXPECT_APPROX_EQ(sender_->BandwidthEstimate() * (1 / 2.885f),
                   sender_->PacingRate(0), 0.01f);

  // BBR uses CWND gain of 2 during STARTUP, hence it will fill the buffer
  // with approximately 1 BDP.  Here, we use 0.8 to give some margin for
  // error.
  EXPECT_GE(queue->bytes_queued(), 0.8 * kTestBdp);

  // Observe increased RTT due to bufferbloat.
  const QuicTime::Delta queueing_delay =
      kTestLinkBandwidth.TransferTime(queue->bytes_queued());
  EXPECT_APPROX_EQ(kTestRtt + queueing_delay, rtt_stats_->latest_rtt(), 0.1f);

  // Transition to the drain phase and verify that it makes the queue
  // have at most a BDP worth of packets.
  simulator_result = simulator_.RunUntilOrTimeout(
      [this]() { return sender_->ExportDebugState().mode != BbrSender::DRAIN; },
      timeout);
  ASSERT_TRUE(simulator_result);
  ASSERT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode);
  EXPECT_LE(queue->bytes_queued(), kTestBdp);

  // Wait for a few round trips and ensure we're in appropriate phase of gain
  // cycling before taking an RTT measurement.
  const QuicRoundTripCount start_round_trip =
      sender_->ExportDebugState().round_trip_count;
  simulator_result = simulator_.RunUntilOrTimeout(
      [this, start_round_trip]() {
        QuicRoundTripCount rounds_passed =
            sender_->ExportDebugState().round_trip_count - start_round_trip;
        return rounds_passed >= 4 &&
               sender_->ExportDebugState().gain_cycle_index == 7;
      },
      timeout);
  ASSERT_TRUE(simulator_result);

  // Observe the bufferbloat go away.
  EXPECT_APPROX_EQ(kTestRtt, rtt_stats_->smoothed_rtt(), 0.1f);
}

// TODO(wub): Re-enable this test once default drain_gain changed to 0.75.
// Verify that the DRAIN phase works correctly.
TEST_F(BbrSenderTest, DISABLED_ShallowDrain) {
  CreateDefaultSetup();
  const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(10);
  // Get the queue at the bottleneck, which is the outgoing queue at the port to
  // which the receiver is connected.
  const simulator::Queue* queue = switch_->port_queue(2);
  bool simulator_result;

  // We have no intention of ever finishing this transfer.
  bbr_sender_.AddBytesToTransfer(100 * 1024 * 1024);

  // Run the startup, and verify that it fills up the queue.
  ASSERT_EQ(BbrSender::STARTUP, sender_->ExportDebugState().mode);
  simulator_result = simulator_.RunUntilOrTimeout(
      [this]() {
        return sender_->ExportDebugState().mode != BbrSender::STARTUP;
      },
      timeout);
  ASSERT_TRUE(simulator_result);
  ASSERT_EQ(BbrSender::DRAIN, sender_->ExportDebugState().mode);
  EXPECT_EQ(0.75 * sender_->BandwidthEstimate(), sender_->PacingRate(0));
  // BBR uses CWND gain of 2.88 during STARTUP, hence it will fill the buffer
  // with approximately 1.88 BDPs.  Here, we use 1.5 to give some margin for
  // error.
  EXPECT_GE(queue->bytes_queued(), 1.5 * kTestBdp);

  // Observe increased RTT due to bufferbloat.
  const QuicTime::Delta queueing_delay =
      kTestLinkBandwidth.TransferTime(queue->bytes_queued());
  EXPECT_APPROX_EQ(kTestRtt + queueing_delay, rtt_stats_->latest_rtt(), 0.1f);

  // Transition to the drain phase and verify that it makes the queue
  // have at most a BDP worth of packets.
  simulator_result = simulator_.RunUntilOrTimeout(
      [this]() { return sender_->ExportDebugState().mode != BbrSender::DRAIN; },
      timeout);
  ASSERT_TRUE(simulator_result);
  ASSERT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode);
  EXPECT_LE(queue->bytes_queued(), kTestBdp);

  // Wait for a few round trips and ensure we're in appropriate phase of gain
  // cycling before taking an RTT measurement.
  const QuicRoundTripCount start_round_trip =
      sender_->ExportDebugState().round_trip_count;
  simulator_result = simulator_.RunUntilOrTimeout(
      [this, start_round_trip]() {
        QuicRoundTripCount rounds_passed =
            sender_->ExportDebugState().round_trip_count - start_round_trip;
        return rounds_passed >= 4 &&
               sender_->ExportDebugState().gain_cycle_index == 7;
      },
      timeout);
  ASSERT_TRUE(simulator_result);

  // Observe the bufferbloat go away.
  EXPECT_APPROX_EQ(kTestRtt, rtt_stats_->smoothed_rtt(), 0.1f);
}

// Verify that the connection enters and exits PROBE_RTT correctly.
TEST_F(BbrSenderTest, ProbeRtt) {
  CreateDefaultSetup();
  DriveOutOfStartup();

  // We have no intention of ever finishing this transfer.
  bbr_sender_.AddBytesToTransfer(100 * 1024 * 1024);

  // Wait until the connection enters PROBE_RTT.
  const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(12);
  bool simulator_result = simulator_.RunUntilOrTimeout(
      [this]() {
        return sender_->ExportDebugState().mode == BbrSender::PROBE_RTT;
      },
      timeout);
  ASSERT_TRUE(simulator_result);
  ASSERT_EQ(BbrSender::PROBE_RTT, sender_->ExportDebugState().mode);

  // Exit PROBE_RTT.
  const QuicTime probe_rtt_start = clock_->Now();
  const QuicTime::Delta time_to_exit_probe_rtt =
      kTestRtt + QuicTime::Delta::FromMilliseconds(200);
  simulator_.RunFor(1.5 * time_to_exit_probe_rtt);
  EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode);
  EXPECT_GE(sender_->ExportDebugState().min_rtt_timestamp, probe_rtt_start);
}

// Ensure that a connection that is app-limited and is at sufficiently low
// bandwidth will not exit high gain phase, and similarly ensure that the
// connection will exit low gain early if the number of bytes in flight is low.
// TODO(crbug.com/1145095): Re-enable this test.
TEST_F(BbrSenderTest, QUIC_TEST_DISABLED_IN_CHROME(InFlightAwareGainCycling)) {
  CreateDefaultSetup();
  DriveOutOfStartup();

  const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(5);
  while (!(sender_->ExportDebugState().gain_cycle_index >= 4 &&
           bbr_sender_.bytes_to_transfer() == 0)) {
    bbr_sender_.AddBytesToTransfer(kTestLinkBandwidth.ToBytesPerSecond());
    ASSERT_TRUE(simulator_.RunUntilOrTimeout(
        [this]() { return bbr_sender_.bytes_to_transfer() == 0; }, timeout));
  }

  // Send at 10% of available rate.  Run for 3 seconds, checking in the middle
  // and at the end.  The pacing gain should be high throughout.
  QuicBandwidth target_bandwidth = 0.1f * kTestLinkBandwidth;
  QuicTime::Delta burst_interval = QuicTime::Delta::FromMilliseconds(300);
  for (int i = 0; i < 2; i++) {
    SendBursts(5, target_bandwidth * burst_interval, burst_interval);
    EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode);
    EXPECT_EQ(0, sender_->ExportDebugState().gain_cycle_index);
    EXPECT_APPROX_EQ(kTestLinkBandwidth,
                     sender_->ExportDebugState().max_bandwidth, 0.02f);
  }

  // Now that in-flight is almost zero and the pacing gain is still above 1,
  // send approximately 1.25 BDPs worth of data.  This should cause the
  // PROBE_BW mode to enter low gain cycle, and exit it earlier than one min_rtt
  // due to running out of data to send.
  bbr_sender_.AddBytesToTransfer(1.3 * kTestBdp);
  ASSERT_TRUE(simulator_.RunUntilOrTimeout(
      [this]() { return sender_->ExportDebugState().gain_cycle_index == 1; },
      timeout));

  simulator_.RunFor(0.75 * sender_->ExportDebugState().min_rtt);
  EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode);
  EXPECT_EQ(2, sender_->ExportDebugState().gain_cycle_index);
}

// Ensure that the pacing rate does not drop at startup.
TEST_F(BbrSenderTest, NoBandwidthDropOnStartup) {
  CreateDefaultSetup();

  const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(5);
  bool simulator_result;

  QuicBandwidth initial_rate = QuicBandwidth::FromBytesAndTimeDelta(
      kInitialCongestionWindowPackets * kDefaultTCPMSS,
      rtt_stats_->initial_rtt());
  EXPECT_GE(sender_->PacingRate(0), initial_rate);

  // Send a packet.
  bbr_sender_.AddBytesToTransfer(1000);
  simulator_result = simulator_.RunUntilOrTimeout(
      [this]() { return receiver_.bytes_received() == 1000; }, timeout);
  ASSERT_TRUE(simulator_result);
  EXPECT_GE(sender_->PacingRate(0), initial_rate);

  // Wait for a while.
  simulator_.RunFor(QuicTime::Delta::FromSeconds(2));
  EXPECT_GE(sender_->PacingRate(0), initial_rate);

  // Send another packet.
  bbr_sender_.AddBytesToTransfer(1000);
  simulator_result = simulator_.RunUntilOrTimeout(
      [this]() { return receiver_.bytes_received() == 2000; }, timeout);
  ASSERT_TRUE(simulator_result);
  EXPECT_GE(sender_->PacingRate(0), initial_rate);
}

// Test exiting STARTUP earlier due to the 1RTT connection option.
TEST_F(BbrSenderTest, SimpleTransfer1RTTStartup) {
  CreateDefaultSetup();

  SetConnectionOption(k1RTT);
  EXPECT_EQ(1u, sender_->num_startup_rtts());

  // Run until the full bandwidth is reached and check how many rounds it was.
  bbr_sender_.AddBytesToTransfer(12 * 1024 * 1024);
  QuicRoundTripCount max_bw_round = 0;
  QuicBandwidth max_bw(QuicBandwidth::Zero());
  bool simulator_result = simulator_.RunUntilOrTimeout(
      [this, &max_bw, &max_bw_round]() {
        if (max_bw < sender_->ExportDebugState().max_bandwidth) {
          max_bw = sender_->ExportDebugState().max_bandwidth;
          max_bw_round = sender_->ExportDebugState().round_trip_count;
        }
        return sender_->ExportDebugState().is_at_full_bandwidth;
      },
      QuicTime::Delta::FromSeconds(5));
  ASSERT_TRUE(simulator_result);
  EXPECT_EQ(BbrSender::DRAIN, sender_->ExportDebugState().mode);
  EXPECT_EQ(1u, sender_->ExportDebugState().round_trip_count - max_bw_round);
  EXPECT_EQ(1u, sender_->ExportDebugState().rounds_without_bandwidth_gain);
  EXPECT_EQ(0u, bbr_sender_.connection()->GetStats().packets_lost);
  EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
}

// Test exiting STARTUP earlier due to the 2RTT connection option.
TEST_F(BbrSenderTest, SimpleTransfer2RTTStartup) {
  CreateDefaultSetup();

  SetConnectionOption(k2RTT);
  EXPECT_EQ(2u, sender_->num_startup_rtts());

  // Run until the full bandwidth is reached and check how many rounds it was.
  bbr_sender_.AddBytesToTransfer(12 * 1024 * 1024);
  QuicRoundTripCount max_bw_round = 0;
  QuicBandwidth max_bw(QuicBandwidth::Zero());
  bool simulator_result = simulator_.RunUntilOrTimeout(
      [this, &max_bw, &max_bw_round]() {
        if (max_bw * 1.001 < sender_->ExportDebugState().max_bandwidth) {
          max_bw = sender_->ExportDebugState().max_bandwidth;
          max_bw_round = sender_->ExportDebugState().round_trip_count;
        }
        return sender_->ExportDebugState().is_at_full_bandwidth;
      },
      QuicTime::Delta::FromSeconds(5));
  ASSERT_TRUE(simulator_result);
  EXPECT_EQ(BbrSender::DRAIN, sender_->ExportDebugState().mode);
  EXPECT_EQ(2u, sender_->ExportDebugState().round_trip_count - max_bw_round);
  EXPECT_EQ(2u, sender_->ExportDebugState().rounds_without_bandwidth_gain);
  EXPECT_EQ(0u, bbr_sender_.connection()->GetStats().packets_lost);
  EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
}

// Test exiting STARTUP earlier upon loss.
TEST_F(BbrSenderTest, SimpleTransferExitStartupOnLoss) {
  CreateDefaultSetup();

  EXPECT_EQ(3u, sender_->num_startup_rtts());

  // Run until the full bandwidth is reached and check how many rounds it was.
  bbr_sender_.AddBytesToTransfer(12 * 1024 * 1024);
  QuicRoundTripCount max_bw_round = 0;
  QuicBandwidth max_bw(QuicBandwidth::Zero());
  bool simulator_result = simulator_.RunUntilOrTimeout(
      [this, &max_bw, &max_bw_round]() {
        if (max_bw * 1.001 < sender_->ExportDebugState().max_bandwidth) {
          max_bw = sender_->ExportDebugState().max_bandwidth;
          max_bw_round = sender_->ExportDebugState().round_trip_count;
        }
        return sender_->ExportDebugState().is_at_full_bandwidth;
      },
      QuicTime::Delta::FromSeconds(5));
  ASSERT_TRUE(simulator_result);
  EXPECT_EQ(BbrSender::DRAIN, sender_->ExportDebugState().mode);
  EXPECT_EQ(3u, sender_->ExportDebugState().round_trip_count - max_bw_round);
  EXPECT_EQ(3u, sender_->ExportDebugState().rounds_without_bandwidth_gain);
  EXPECT_EQ(0u, bbr_sender_.connection()->GetStats().packets_lost);
  EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
}

// Test exiting STARTUP earlier upon loss with a small buffer.
TEST_F(BbrSenderTest, SimpleTransferExitStartupOnLossSmallBuffer) {
  CreateSmallBufferSetup();

  EXPECT_EQ(3u, sender_->num_startup_rtts());

  // Run until the full bandwidth is reached and check how many rounds it was.
  bbr_sender_.AddBytesToTransfer(12 * 1024 * 1024);
  QuicRoundTripCount max_bw_round = 0;
  QuicBandwidth max_bw(QuicBandwidth::Zero());
  bool simulator_result = simulator_.RunUntilOrTimeout(
      [this, &max_bw, &max_bw_round]() {
        if (max_bw < sender_->ExportDebugState().max_bandwidth) {
          max_bw = sender_->ExportDebugState().max_bandwidth;
          max_bw_round = sender_->ExportDebugState().round_trip_count;
        }
        return sender_->ExportDebugState().is_at_full_bandwidth;
      },
      QuicTime::Delta::FromSeconds(5));
  ASSERT_TRUE(simulator_result);
  EXPECT_EQ(BbrSender::DRAIN, sender_->ExportDebugState().mode);
  EXPECT_GE(2u, sender_->ExportDebugState().round_trip_count - max_bw_round);
  EXPECT_EQ(1u, sender_->ExportDebugState().rounds_without_bandwidth_gain);
  EXPECT_NE(0u, bbr_sender_.connection()->GetStats().packets_lost);
  EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
}

TEST_F(BbrSenderTest, DerivedPacingGainStartup) {
  CreateDefaultSetup();

  SetConnectionOption(kBBQ1);
  EXPECT_EQ(3u, sender_->num_startup_rtts());
  // Verify that Sender is in slow start.
  EXPECT_TRUE(sender_->InSlowStart());
  // Verify that pacing rate is based on the initial RTT.
  QuicBandwidth expected_pacing_rate = QuicBandwidth::FromBytesAndTimeDelta(
      2.773 * kDefaultWindowTCP, rtt_stats_->initial_rtt());
  EXPECT_APPROX_EQ(expected_pacing_rate.ToBitsPerSecond(),
                   sender_->PacingRate(0).ToBitsPerSecond(), 0.01f);

  // Run until the full bandwidth is reached and check how many rounds it was.
  bbr_sender_.AddBytesToTransfer(12 * 1024 * 1024);
  bool simulator_result = simulator_.RunUntilOrTimeout(
      [this]() { return sender_->ExportDebugState().is_at_full_bandwidth; },
      QuicTime::Delta::FromSeconds(5));
  ASSERT_TRUE(simulator_result);
  EXPECT_EQ(BbrSender::DRAIN, sender_->ExportDebugState().mode);
  EXPECT_EQ(3u, sender_->ExportDebugState().rounds_without_bandwidth_gain);
  EXPECT_APPROX_EQ(kTestLinkBandwidth,
                   sender_->ExportDebugState().max_bandwidth, 0.01f);
  EXPECT_EQ(0u, bbr_sender_.connection()->GetStats().packets_lost);
  EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
}

TEST_F(BbrSenderTest, DerivedCWNDGainStartup) {
  CreateSmallBufferSetup();

  EXPECT_EQ(3u, sender_->num_startup_rtts());
  // Verify that Sender is in slow start.
  EXPECT_TRUE(sender_->InSlowStart());
  // Verify that pacing rate is based on the initial RTT.
  QuicBandwidth expected_pacing_rate = QuicBandwidth::FromBytesAndTimeDelta(
      2.885 * kDefaultWindowTCP, rtt_stats_->initial_rtt());
  EXPECT_APPROX_EQ(expected_pacing_rate.ToBitsPerSecond(),
                   sender_->PacingRate(0).ToBitsPerSecond(), 0.01f);

  // Run until the full bandwidth is reached and check how many rounds it was.
  bbr_sender_.AddBytesToTransfer(12 * 1024 * 1024);
  bool simulator_result = simulator_.RunUntilOrTimeout(
      [this]() { return sender_->ExportDebugState().is_at_full_bandwidth; },
      QuicTime::Delta::FromSeconds(5));
  ASSERT_TRUE(simulator_result);
  EXPECT_EQ(BbrSender::DRAIN, sender_->ExportDebugState().mode);
  if (!bbr_sender_.connection()->GetStats().bbr_exit_startup_due_to_loss) {
    EXPECT_EQ(3u, sender_->ExportDebugState().rounds_without_bandwidth_gain);
  }
  EXPECT_APPROX_EQ(kTestLinkBandwidth,
                   sender_->ExportDebugState().max_bandwidth, 0.01f);
  float loss_rate =
      static_cast<float>(bbr_sender_.connection()->GetStats().packets_lost) /
      bbr_sender_.connection()->GetStats().packets_sent;
  EXPECT_LT(loss_rate, 0.15f);
  EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
  // Expect an SRTT less than 2.7 * Min RTT on exit from STARTUP.
  EXPECT_GT(kTestRtt * 2.7, rtt_stats_->smoothed_rtt());
}

TEST_F(BbrSenderTest, AckAggregationInStartup) {
  CreateDefaultSetup();

  SetConnectionOption(kBBQ3);
  EXPECT_EQ(3u, sender_->num_startup_rtts());
  // Verify that Sender is in slow start.
  EXPECT_TRUE(sender_->InSlowStart());
  // Verify that pacing rate is based on the initial RTT.
  QuicBandwidth expected_pacing_rate = QuicBandwidth::FromBytesAndTimeDelta(
      2.885 * kDefaultWindowTCP, rtt_stats_->initial_rtt());
  EXPECT_APPROX_EQ(expected_pacing_rate.ToBitsPerSecond(),
                   sender_->PacingRate(0).ToBitsPerSecond(), 0.01f);

  // Run until the full bandwidth is reached and check how many rounds it was.
  bbr_sender_.AddBytesToTransfer(12 * 1024 * 1024);
  bool simulator_result = simulator_.RunUntilOrTimeout(
      [this]() { return sender_->ExportDebugState().is_at_full_bandwidth; },
      QuicTime::Delta::FromSeconds(5));
  ASSERT_TRUE(simulator_result);
  EXPECT_EQ(BbrSender::DRAIN, sender_->ExportDebugState().mode);
  EXPECT_EQ(3u, sender_->ExportDebugState().rounds_without_bandwidth_gain);
  EXPECT_APPROX_EQ(kTestLinkBandwidth,
                   sender_->ExportDebugState().max_bandwidth, 0.01f);
  EXPECT_EQ(0u, bbr_sender_.connection()->GetStats().packets_lost);
  EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
}

// Test that two BBR flows started slightly apart from each other terminate.
TEST_F(BbrSenderTest, SimpleCompetition) {
  const QuicByteCount transfer_size = 10 * 1024 * 1024;
  const QuicTime::Delta transfer_time =
      kTestLinkBandwidth.TransferTime(transfer_size);
  CreateBbrVsBbrSetup();

  // Transfer 10% of data in first transfer.
  bbr_sender_.AddBytesToTransfer(transfer_size);
  bool simulator_result = simulator_.RunUntilOrTimeout(
      [this]() { return receiver_.bytes_received() >= 0.1 * transfer_size; },
      transfer_time);
  ASSERT_TRUE(simulator_result);

  // Start the second transfer and wait until both finish.
  competing_sender_.AddBytesToTransfer(transfer_size);
  simulator_result = simulator_.RunUntilOrTimeout(
      [this]() {
        return receiver_.bytes_received() == transfer_size &&
               competing_receiver_.bytes_received() == transfer_size;
      },
      3 * transfer_time);
  ASSERT_TRUE(simulator_result);
}

// Test that BBR can resume bandwidth from cached network parameters.
TEST_F(BbrSenderTest, ResumeConnectionState) {
  CreateDefaultSetup();

  bbr_sender_.connection()->AdjustNetworkParameters(
      SendAlgorithmInterface::NetworkParams(kTestLinkBandwidth, kTestRtt,
                                            false));
  EXPECT_EQ(kTestLinkBandwidth * kTestRtt,
            sender_->ExportDebugState().congestion_window);

  EXPECT_EQ(kTestLinkBandwidth, sender_->PacingRate(/*bytes_in_flight=*/0));

  EXPECT_APPROX_EQ(kTestRtt, sender_->ExportDebugState().min_rtt, 0.01f);

  DriveOutOfStartup();
}

// Test with a min CWND of 1 instead of 4 packets.
TEST_F(BbrSenderTest, ProbeRTTMinCWND1) {
  CreateDefaultSetup();
  SetConnectionOption(kMIN1);
  DriveOutOfStartup();

  // We have no intention of ever finishing this transfer.
  bbr_sender_.AddBytesToTransfer(100 * 1024 * 1024);

  // Wait until the connection enters PROBE_RTT.
  const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(12);
  bool simulator_result = simulator_.RunUntilOrTimeout(
      [this]() {
        return sender_->ExportDebugState().mode == BbrSender::PROBE_RTT;
      },
      timeout);
  ASSERT_TRUE(simulator_result);
  ASSERT_EQ(BbrSender::PROBE_RTT, sender_->ExportDebugState().mode);
  // The PROBE_RTT CWND should be 1 if the min CWND is 1.
  EXPECT_EQ(kDefaultTCPMSS, sender_->GetCongestionWindow());

  // Exit PROBE_RTT.
  const QuicTime probe_rtt_start = clock_->Now();
  const QuicTime::Delta time_to_exit_probe_rtt =
      kTestRtt + QuicTime::Delta::FromMilliseconds(200);
  simulator_.RunFor(1.5 * time_to_exit_probe_rtt);
  EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode);
  EXPECT_GE(sender_->ExportDebugState().min_rtt_timestamp, probe_rtt_start);
}

TEST_F(BbrSenderTest, StartupStats) {
  CreateDefaultSetup();

  DriveOutOfStartup();
  ASSERT_FALSE(sender_->InSlowStart());

  const QuicConnectionStats& stats = bbr_sender_.connection()->GetStats();
  EXPECT_EQ(1u, stats.slowstart_count);
  EXPECT_THAT(stats.slowstart_num_rtts, AllOf(Ge(5u), Le(15u)));
  EXPECT_THAT(stats.slowstart_packets_sent, AllOf(Ge(100u), Le(1000u)));
  EXPECT_THAT(stats.slowstart_bytes_sent, AllOf(Ge(100000u), Le(1000000u)));
  EXPECT_LE(stats.slowstart_packets_lost, 10u);
  EXPECT_LE(stats.slowstart_bytes_lost, 10000u);
  EXPECT_FALSE(stats.slowstart_duration.IsRunning());
  EXPECT_THAT(stats.slowstart_duration.GetTotalElapsedTime(),
              AllOf(Ge(QuicTime::Delta::FromMilliseconds(500)),
                    Le(QuicTime::Delta::FromMilliseconds(1500))));
  EXPECT_EQ(stats.slowstart_duration.GetTotalElapsedTime(),
            QuicConnectionPeer::GetSentPacketManager(bbr_sender_.connection())
                ->GetSlowStartDuration());
}

// Regression test for b/143540157.
TEST_F(BbrSenderTest, RecalculatePacingRateOnCwndChange1RTT) {
  CreateDefaultSetup();

  bbr_sender_.AddBytesToTransfer(1 * 1024 * 1024);
  // Wait until an ACK comes back.
  const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(5);
  bool simulator_result = simulator_.RunUntilOrTimeout(
      [this]() { return !sender_->ExportDebugState().min_rtt.IsZero(); },
      timeout);
  ASSERT_TRUE(simulator_result);
  const QuicByteCount previous_cwnd =
      sender_->ExportDebugState().congestion_window;

  // Bootstrap cwnd.
  bbr_sender_.connection()->AdjustNetworkParameters(
      SendAlgorithmInterface::NetworkParams(kTestLinkBandwidth,
                                            QuicTime::Delta::Zero(), false));
  EXPECT_LT(previous_cwnd, sender_->ExportDebugState().congestion_window);

  // Verify pacing rate is re-calculated based on the new cwnd and min_rtt.
  EXPECT_APPROX_EQ(QuicBandwidth::FromBytesAndTimeDelta(
                       sender_->ExportDebugState().congestion_window,
                       sender_->ExportDebugState().min_rtt),
                   sender_->PacingRate(/*bytes_in_flight=*/0), 0.01f);
}

TEST_F(BbrSenderTest, RecalculatePacingRateOnCwndChange0RTT) {
  CreateDefaultSetup();
  // Initial RTT is available.
  const_cast<RttStats*>(rtt_stats_)->set_initial_rtt(kTestRtt);

  // Bootstrap cwnd.
  bbr_sender_.connection()->AdjustNetworkParameters(
      SendAlgorithmInterface::NetworkParams(kTestLinkBandwidth,
                                            QuicTime::Delta::Zero(), false));
  EXPECT_LT(kInitialCongestionWindowPackets * kDefaultTCPMSS,
            sender_->ExportDebugState().congestion_window);
  // No Rtt sample is available.
  EXPECT_TRUE(sender_->ExportDebugState().min_rtt.IsZero());

  // Verify pacing rate is re-calculated based on the new cwnd and initial
  // RTT.
  EXPECT_APPROX_EQ(QuicBandwidth::FromBytesAndTimeDelta(
                       sender_->ExportDebugState().congestion_window,
                       rtt_stats_->initial_rtt()),
                   sender_->PacingRate(/*bytes_in_flight=*/0), 0.01f);
}

TEST_F(BbrSenderTest, MitigateCwndBootstrappingOvershoot) {
  CreateDefaultSetup();
  bbr_sender_.AddBytesToTransfer(1 * 1024 * 1024);

  // Wait until an ACK comes back.
  const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(5);
  bool simulator_result = simulator_.RunUntilOrTimeout(
      [this]() { return !sender_->ExportDebugState().min_rtt.IsZero(); },
      timeout);
  ASSERT_TRUE(simulator_result);

  // Bootstrap cwnd by a overly large bandwidth sample.
  bbr_sender_.connection()->AdjustNetworkParameters(
      SendAlgorithmInterface::NetworkParams(8 * kTestLinkBandwidth,
                                            QuicTime::Delta::Zero(), false));
  QuicBandwidth pacing_rate = sender_->PacingRate(0);
  EXPECT_EQ(8 * kTestLinkBandwidth, pacing_rate);

  // Wait until pacing_rate decreases.
  simulator_result = simulator_.RunUntilOrTimeout(
      [this, pacing_rate]() { return sender_->PacingRate(0) < pacing_rate; },
      timeout);
  ASSERT_TRUE(simulator_result);
  EXPECT_EQ(BbrSender::STARTUP, sender_->ExportDebugState().mode);
  if (GetQuicReloadableFlag(quic_conservative_cwnd_and_pacing_gains)) {
    EXPECT_APPROX_EQ(2.0f * sender_->BandwidthEstimate(),
                     sender_->PacingRate(0), 0.01f);
  } else {
    EXPECT_APPROX_EQ(2.885f * sender_->BandwidthEstimate(),
                     sender_->PacingRate(0), 0.01f);
  }
}

TEST_F(BbrSenderTest, 200InitialCongestionWindowWithNetworkParameterAdjusted) {
  CreateDefaultSetup();

  bbr_sender_.AddBytesToTransfer(1 * 1024 * 1024);
  // Wait until an ACK comes back.
  const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(5);
  bool simulator_result = simulator_.RunUntilOrTimeout(
      [this]() { return !sender_->ExportDebugState().min_rtt.IsZero(); },
      timeout);
  ASSERT_TRUE(simulator_result);

  // Bootstrap cwnd by a overly large bandwidth sample.
  bbr_sender_.connection()->AdjustNetworkParameters(
      SendAlgorithmInterface::NetworkParams(1024 * kTestLinkBandwidth,
                                            QuicTime::Delta::Zero(), false));
  // Verify cwnd is capped at 200.
  EXPECT_EQ(200 * kDefaultTCPMSS,
            sender_->ExportDebugState().congestion_window);
  EXPECT_GT(1024 * kTestLinkBandwidth, sender_->PacingRate(0));
}

TEST_F(BbrSenderTest, 100InitialCongestionWindowFromNetworkParameter) {
  CreateDefaultSetup();

  bbr_sender_.AddBytesToTransfer(1 * 1024 * 1024);
  // Wait until an ACK comes back.
  const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(5);
  bool simulator_result = simulator_.RunUntilOrTimeout(
      [this]() { return !sender_->ExportDebugState().min_rtt.IsZero(); },
      timeout);
  ASSERT_TRUE(simulator_result);

  // Bootstrap cwnd by a overly large bandwidth sample.
  SendAlgorithmInterface::NetworkParams network_params(
      1024 * kTestLinkBandwidth, QuicTime::Delta::Zero(), false);
  network_params.max_initial_congestion_window = 100;
  bbr_sender_.connection()->AdjustNetworkParameters(network_params);
  // Verify cwnd is capped at 100.
  EXPECT_EQ(100 * kDefaultTCPMSS,
            sender_->ExportDebugState().congestion_window);
  EXPECT_GT(1024 * kTestLinkBandwidth, sender_->PacingRate(0));
}

TEST_F(BbrSenderTest, 100InitialCongestionWindowWithNetworkParameterAdjusted) {
  SetConnectionOption(kICW1);
  CreateDefaultSetup();

  bbr_sender_.AddBytesToTransfer(1 * 1024 * 1024);
  // Wait until an ACK comes back.
  const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(5);
  bool simulator_result = simulator_.RunUntilOrTimeout(
      [this]() { return !sender_->ExportDebugState().min_rtt.IsZero(); },
      timeout);
  ASSERT_TRUE(simulator_result);

  // Bootstrap cwnd by a overly large bandwidth sample.
  bbr_sender_.connection()->AdjustNetworkParameters(
      SendAlgorithmInterface::NetworkParams(1024 * kTestLinkBandwidth,
                                            QuicTime::Delta::Zero(), false));
  // Verify cwnd is capped at 100.
  EXPECT_EQ(100 * kDefaultTCPMSS,
            sender_->ExportDebugState().congestion_window);
  EXPECT_GT(1024 * kTestLinkBandwidth, sender_->PacingRate(0));
}

// Ensures bandwidth estimate does not change after a loss only event.
// Regression test for b/151239871.
TEST_F(BbrSenderTest, LossOnlyCongestionEvent) {
  CreateDefaultSetup();

  DriveOutOfStartup();
  EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);

  // Send some bursts, each burst increments round count by 1, since it only
  // generates small, app-limited samples, the max_bandwidth_ will not be
  // updated. At the end of all bursts, all estimates in max_bandwidth_ will
  // look very old such that any Update() will reset all estimates.
  SendBursts(20, 512, QuicTime::Delta::FromSeconds(3));

  QuicUnackedPacketMap* unacked_packets =
      QuicSentPacketManagerPeer::GetUnackedPacketMap(
          QuicConnectionPeer::GetSentPacketManager(bbr_sender_.connection()));
  // Run until we have something in flight.
  bbr_sender_.AddBytesToTransfer(50 * 1024 * 1024);
  bool simulator_result = simulator_.RunUntilOrTimeout(
      [&]() { return unacked_packets->bytes_in_flight() > 0; },
      QuicTime::Delta::FromSeconds(5));
  ASSERT_TRUE(simulator_result);

  const QuicBandwidth prior_bandwidth_estimate = sender_->BandwidthEstimate();
  EXPECT_APPROX_EQ(kTestLinkBandwidth, prior_bandwidth_estimate, 0.01f);

  // Lose the least unacked packet.
  LostPacketVector lost_packets;
  lost_packets.emplace_back(
      bbr_sender_.connection()->sent_packet_manager().GetLeastUnacked(),
      kDefaultMaxPacketSize);

  QuicTime now = simulator_.GetClock()->Now() + kTestRtt * 0.25;
  sender_->OnCongestionEvent(false, unacked_packets->bytes_in_flight(), now, {},
                             lost_packets, 0, 0);

  // Bandwidth estimate should not change for the loss only event.
  EXPECT_EQ(prior_bandwidth_estimate, sender_->BandwidthEstimate());
}

TEST_F(BbrSenderTest, EnableOvershootingDetection) {
  SetConnectionOption(kDTOS);
  CreateSmallBufferSetup();
  // Set a overly large initial cwnd.
  sender_->SetInitialCongestionWindowInPackets(200);
  const QuicConnectionStats& stats = bbr_sender_.connection()->GetStats();
  EXPECT_FALSE(stats.overshooting_detected_with_network_parameters_adjusted);
  DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(30));

  // Verify overshooting is detected.
  EXPECT_TRUE(stats.overshooting_detected_with_network_parameters_adjusted);
}

}  // namespace test
}  // namespace quic
