// Copyright 2014 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/quic_sustained_bandwidth_recorder.h"

#include "quiche/quic/core/quic_bandwidth.h"
#include "quiche/quic/core/quic_time.h"
#include "quiche/quic/platform/api/quic_test.h"

namespace quic {
namespace test {
namespace {

class QuicSustainedBandwidthRecorderTest : public QuicTest {};

TEST_F(QuicSustainedBandwidthRecorderTest, BandwidthEstimates) {
  QuicSustainedBandwidthRecorder recorder;
  EXPECT_FALSE(recorder.HasEstimate());

  QuicTime estimate_time = QuicTime::Zero();
  QuicWallTime wall_time = QuicWallTime::Zero();
  QuicTime::Delta srtt = QuicTime::Delta::FromMilliseconds(150);
  const int kBandwidthBitsPerSecond = 12345678;
  QuicBandwidth bandwidth =
      QuicBandwidth::FromBitsPerSecond(kBandwidthBitsPerSecond);

  bool in_recovery = false;
  bool in_slow_start = false;

  // This triggers recording, but should not yield a valid estimate yet.
  recorder.RecordEstimate(in_recovery, in_slow_start, bandwidth, estimate_time,
                          wall_time, srtt);
  EXPECT_FALSE(recorder.HasEstimate());

  // Send a second reading, again this should not result in a valid estimate,
  // as not enough time has passed.
  estimate_time = estimate_time + srtt;
  recorder.RecordEstimate(in_recovery, in_slow_start, bandwidth, estimate_time,
                          wall_time, srtt);
  EXPECT_FALSE(recorder.HasEstimate());

  // Now 3 * kSRTT has elapsed since first recording, expect a valid estimate.
  estimate_time = estimate_time + srtt;
  estimate_time = estimate_time + srtt;
  recorder.RecordEstimate(in_recovery, in_slow_start, bandwidth, estimate_time,
                          wall_time, srtt);
  EXPECT_TRUE(recorder.HasEstimate());
  EXPECT_EQ(recorder.BandwidthEstimate(), bandwidth);
  EXPECT_EQ(recorder.BandwidthEstimate(), recorder.MaxBandwidthEstimate());

  // Resetting, and sending a different estimate will only change output after
  // a further 3 * kSRTT has passed.
  QuicBandwidth second_bandwidth =
      QuicBandwidth::FromBitsPerSecond(2 * kBandwidthBitsPerSecond);
  // Reset the recorder by passing in a measurement while in recovery.
  in_recovery = true;
  recorder.RecordEstimate(in_recovery, in_slow_start, bandwidth, estimate_time,
                          wall_time, srtt);
  in_recovery = false;
  recorder.RecordEstimate(in_recovery, in_slow_start, bandwidth, estimate_time,
                          wall_time, srtt);
  EXPECT_EQ(recorder.BandwidthEstimate(), bandwidth);

  estimate_time = estimate_time + 3 * srtt;
  const int64_t kSeconds = 556677;
  QuicWallTime second_bandwidth_wall_time =
      QuicWallTime::FromUNIXSeconds(kSeconds);
  recorder.RecordEstimate(in_recovery, in_slow_start, second_bandwidth,
                          estimate_time, second_bandwidth_wall_time, srtt);
  EXPECT_EQ(recorder.BandwidthEstimate(), second_bandwidth);
  EXPECT_EQ(recorder.BandwidthEstimate(), recorder.MaxBandwidthEstimate());
  EXPECT_EQ(recorder.MaxBandwidthTimestamp(), kSeconds);

  // Reset again, this time recording a lower bandwidth than before.
  QuicBandwidth third_bandwidth =
      QuicBandwidth::FromBitsPerSecond(0.5 * kBandwidthBitsPerSecond);
  // Reset the recorder by passing in an unreliable measurement.
  recorder.RecordEstimate(in_recovery, in_slow_start, third_bandwidth,
                          estimate_time, wall_time, srtt);
  recorder.RecordEstimate(in_recovery, in_slow_start, third_bandwidth,
                          estimate_time, wall_time, srtt);
  EXPECT_EQ(recorder.BandwidthEstimate(), third_bandwidth);

  estimate_time = estimate_time + 3 * srtt;
  recorder.RecordEstimate(in_recovery, in_slow_start, third_bandwidth,
                          estimate_time, wall_time, srtt);
  EXPECT_EQ(recorder.BandwidthEstimate(), third_bandwidth);

  // Max bandwidth should not have changed.
  EXPECT_LT(third_bandwidth, second_bandwidth);
  EXPECT_EQ(recorder.MaxBandwidthEstimate(), second_bandwidth);
  EXPECT_EQ(recorder.MaxBandwidthTimestamp(), kSeconds);
}

TEST_F(QuicSustainedBandwidthRecorderTest, SlowStart) {
  // Verify that slow start status is correctly recorded.
  QuicSustainedBandwidthRecorder recorder;
  EXPECT_FALSE(recorder.HasEstimate());

  QuicTime estimate_time = QuicTime::Zero();
  QuicWallTime wall_time = QuicWallTime::Zero();
  QuicTime::Delta srtt = QuicTime::Delta::FromMilliseconds(150);
  const int kBandwidthBitsPerSecond = 12345678;
  QuicBandwidth bandwidth =
      QuicBandwidth::FromBitsPerSecond(kBandwidthBitsPerSecond);

  bool in_recovery = false;
  bool in_slow_start = true;

  // This triggers recording, but should not yield a valid estimate yet.
  recorder.RecordEstimate(in_recovery, in_slow_start, bandwidth, estimate_time,
                          wall_time, srtt);

  // Now 3 * kSRTT has elapsed since first recording, expect a valid estimate.
  estimate_time = estimate_time + 3 * srtt;
  recorder.RecordEstimate(in_recovery, in_slow_start, bandwidth, estimate_time,
                          wall_time, srtt);
  EXPECT_TRUE(recorder.HasEstimate());
  EXPECT_TRUE(recorder.EstimateRecordedDuringSlowStart());

  // Now send another estimate, this time not in slow start.
  estimate_time = estimate_time + 3 * srtt;
  in_slow_start = false;
  recorder.RecordEstimate(in_recovery, in_slow_start, bandwidth, estimate_time,
                          wall_time, srtt);
  EXPECT_TRUE(recorder.HasEstimate());
  EXPECT_FALSE(recorder.EstimateRecordedDuringSlowStart());
}

}  // namespace
}  // namespace test
}  // namespace quic
