/*
 *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */
#include "modules/remote_bitrate_estimator/aimd_rate_control.h"

#include "api/units/data_rate.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "test/explicit_key_value_config.h"
#include "test/gtest.h"

namespace webrtc {
namespace {

using ::webrtc::test::ExplicitKeyValueConfig;

constexpr Timestamp kInitialTime = Timestamp::Millis(123'456);

constexpr TimeDelta kMinBwePeriod = TimeDelta::Seconds(2);
constexpr TimeDelta kDefaultPeriod = TimeDelta::Seconds(3);
constexpr TimeDelta kMaxBwePeriod = TimeDelta::Seconds(50);

// After an overuse, we back off to 85% to the received bitrate.
constexpr double kFractionAfterOveruse = 0.85;

}  // namespace

TEST(AimdRateControlTest, MinNearMaxIncreaseRateOnLowBandwith) {
  AimdRateControl aimd_rate_control(ExplicitKeyValueConfig(""));
  aimd_rate_control.SetEstimate(DataRate::BitsPerSec(30'000), kInitialTime);
  EXPECT_EQ(aimd_rate_control.GetNearMaxIncreaseRateBpsPerSecond(), 4'000);
}

TEST(AimdRateControlTest, NearMaxIncreaseRateIs5kbpsOn90kbpsAnd200msRtt) {
  AimdRateControl aimd_rate_control(ExplicitKeyValueConfig(""));
  aimd_rate_control.SetEstimate(DataRate::BitsPerSec(90'000), kInitialTime);
  EXPECT_EQ(aimd_rate_control.GetNearMaxIncreaseRateBpsPerSecond(), 5'000);
}

TEST(AimdRateControlTest, NearMaxIncreaseRateIs5kbpsOn60kbpsAnd100msRtt) {
  AimdRateControl aimd_rate_control(ExplicitKeyValueConfig(""));
  aimd_rate_control.SetEstimate(DataRate::BitsPerSec(60'000), kInitialTime);
  aimd_rate_control.SetRtt(TimeDelta::Millis(100));
  EXPECT_EQ(aimd_rate_control.GetNearMaxIncreaseRateBpsPerSecond(), 5'000);
}

TEST(AimdRateControlTest, GetIncreaseRateAndBandwidthPeriod) {
  AimdRateControl aimd_rate_control(ExplicitKeyValueConfig(""));
  constexpr DataRate kBitrate = DataRate::BitsPerSec(300'000);
  aimd_rate_control.SetEstimate(kBitrate, kInitialTime);
  aimd_rate_control.Update({BandwidthUsage::kBwOverusing, kBitrate},
                           kInitialTime);
  EXPECT_NEAR(aimd_rate_control.GetNearMaxIncreaseRateBpsPerSecond(), 14'000,
              1'000);
  EXPECT_EQ(aimd_rate_control.GetExpectedBandwidthPeriod(), kDefaultPeriod);
}

TEST(AimdRateControlTest, BweLimitedByAckedBitrate) {
  AimdRateControl aimd_rate_control(ExplicitKeyValueConfig(""));
  constexpr DataRate kAckedBitrate = DataRate::BitsPerSec(10'000);
  Timestamp now = kInitialTime;
  aimd_rate_control.SetEstimate(kAckedBitrate, now);
  while (now - kInitialTime < TimeDelta::Seconds(20)) {
    aimd_rate_control.Update({BandwidthUsage::kBwNormal, kAckedBitrate}, now);
    now += TimeDelta::Millis(100);
  }
  ASSERT_TRUE(aimd_rate_control.ValidEstimate());
  EXPECT_EQ(aimd_rate_control.LatestEstimate(),
            1.5 * kAckedBitrate + DataRate::BitsPerSec(10'000));
}

TEST(AimdRateControlTest, BweNotLimitedByDecreasingAckedBitrate) {
  AimdRateControl aimd_rate_control(ExplicitKeyValueConfig(""));
  constexpr DataRate kAckedBitrate = DataRate::BitsPerSec(10'000);
  Timestamp now = kInitialTime;
  aimd_rate_control.SetEstimate(kAckedBitrate, now);
  while (now - kInitialTime < TimeDelta::Seconds(20)) {
    aimd_rate_control.Update({BandwidthUsage::kBwNormal, kAckedBitrate}, now);
    now += TimeDelta::Millis(100);
  }
  ASSERT_TRUE(aimd_rate_control.ValidEstimate());
  // If the acked bitrate decreases the BWE shouldn't be reduced to 1.5x
  // what's being acked, but also shouldn't get to increase more.
  DataRate prev_estimate = aimd_rate_control.LatestEstimate();
  aimd_rate_control.Update({BandwidthUsage::kBwNormal, kAckedBitrate / 2}, now);
  DataRate new_estimate = aimd_rate_control.LatestEstimate();
  EXPECT_EQ(new_estimate, prev_estimate);
  EXPECT_NEAR(new_estimate.bps(),
              (1.5 * kAckedBitrate + DataRate::BitsPerSec(10'000)).bps(),
              2'000);
}

TEST(AimdRateControlTest, DefaultPeriodUntilFirstOveruse) {
  AimdRateControl aimd_rate_control(ExplicitKeyValueConfig(""));
  aimd_rate_control.SetStartBitrate(DataRate::KilobitsPerSec(300));
  EXPECT_EQ(aimd_rate_control.GetExpectedBandwidthPeriod(), kDefaultPeriod);
  aimd_rate_control.Update(
      {BandwidthUsage::kBwOverusing, DataRate::KilobitsPerSec(280)},
      kInitialTime);
  EXPECT_NE(aimd_rate_control.GetExpectedBandwidthPeriod(), kDefaultPeriod);
}

TEST(AimdRateControlTest, ExpectedPeriodAfter20kbpsDropAnd5kbpsIncrease) {
  AimdRateControl aimd_rate_control(ExplicitKeyValueConfig(""));
  constexpr DataRate kInitialBitrate = DataRate::BitsPerSec(110'000);
  Timestamp now = kInitialTime;
  aimd_rate_control.SetEstimate(kInitialBitrate, now);
  now += TimeDelta::Millis(100);
  // Make the bitrate drop by 20 kbps to get to 90 kbps.
  // The rate increase at 90 kbps should be 5 kbps, so the period should be 4 s.
  const DataRate kAckedBitrate =
      (kInitialBitrate - DataRate::BitsPerSec(20'000)) / kFractionAfterOveruse;
  aimd_rate_control.Update({BandwidthUsage::kBwOverusing, kAckedBitrate}, now);
  EXPECT_EQ(aimd_rate_control.GetNearMaxIncreaseRateBpsPerSecond(), 5'000);
  EXPECT_EQ(aimd_rate_control.GetExpectedBandwidthPeriod(),
            TimeDelta::Seconds(4));
}

TEST(AimdRateControlTest, BandwidthPeriodIsNotBelowMin) {
  AimdRateControl aimd_rate_control(ExplicitKeyValueConfig(""));
  constexpr DataRate kInitialBitrate = DataRate::BitsPerSec(10'000);
  Timestamp now = kInitialTime;
  aimd_rate_control.SetEstimate(kInitialBitrate, now);
  now += TimeDelta::Millis(100);
  // Make a small (1.5 kbps) bitrate drop to 8.5 kbps.
  aimd_rate_control.Update(
      {BandwidthUsage::kBwOverusing, kInitialBitrate - DataRate::BitsPerSec(1)},
      now);
  EXPECT_EQ(aimd_rate_control.GetExpectedBandwidthPeriod(), kMinBwePeriod);
}

TEST(AimdRateControlTest, BandwidthPeriodIsNotAboveMaxNoSmoothingExp) {
  AimdRateControl aimd_rate_control(ExplicitKeyValueConfig(""));
  constexpr DataRate kInitialBitrate = DataRate::BitsPerSec(10'010'000);
  Timestamp now = kInitialTime;
  aimd_rate_control.SetEstimate(kInitialBitrate, now);
  now += TimeDelta::Millis(100);
  // Make a large (10 Mbps) bitrate drop to 10 kbps.
  const DataRate kAckedBitrate =
      DataRate::BitsPerSec(10'000) / kFractionAfterOveruse;
  aimd_rate_control.Update({BandwidthUsage::kBwOverusing, kAckedBitrate}, now);
  EXPECT_EQ(aimd_rate_control.GetExpectedBandwidthPeriod(), kMaxBwePeriod);
}

TEST(AimdRateControlTest, SendingRateBoundedWhenThroughputNotEstimated) {
  AimdRateControl aimd_rate_control(ExplicitKeyValueConfig(""));
  constexpr DataRate kInitialBitrate = DataRate::BitsPerSec(123'000);
  Timestamp now = kInitialTime;
  aimd_rate_control.Update({BandwidthUsage::kBwNormal, kInitialBitrate}, now);
  // AimdRateControl sets the initial bit rate to what it receives after
  // five seconds has passed.
  // TODO(bugs.webrtc.org/9379): The comment in the AimdRateControl does not
  // match the constant.
  constexpr TimeDelta kInitializationTime = TimeDelta::Seconds(5);
  now += (kInitializationTime + TimeDelta::Millis(1));
  aimd_rate_control.Update({BandwidthUsage::kBwNormal, kInitialBitrate}, now);
  for (int i = 0; i < 100; ++i) {
    aimd_rate_control.Update({BandwidthUsage::kBwNormal, absl::nullopt}, now);
    now += TimeDelta::Millis(100);
  }
  EXPECT_LE(aimd_rate_control.LatestEstimate(),
            kInitialBitrate * 1.5 + DataRate::BitsPerSec(10'000));
}

TEST(AimdRateControlTest, EstimateDoesNotIncreaseInAlr) {
  // When alr is detected, the delay based estimator is not allowed to increase
  // bwe since there will be no feedback from the network if the new estimate
  // is correct.
  ExplicitKeyValueConfig field_trials(
      "WebRTC-DontIncreaseDelayBasedBweInAlr/Enabled/");
  AimdRateControl aimd_rate_control(field_trials, /*send_side=*/true);
  Timestamp now = kInitialTime;
  constexpr DataRate kInitialBitrate = DataRate::BitsPerSec(123'000);
  aimd_rate_control.SetEstimate(kInitialBitrate, now);
  aimd_rate_control.SetInApplicationLimitedRegion(true);
  aimd_rate_control.Update({BandwidthUsage::kBwNormal, kInitialBitrate}, now);
  ASSERT_EQ(aimd_rate_control.LatestEstimate(), kInitialBitrate);

  for (int i = 0; i < 100; ++i) {
    aimd_rate_control.Update({BandwidthUsage::kBwNormal, absl::nullopt}, now);
    now += TimeDelta::Millis(100);
  }
  EXPECT_EQ(aimd_rate_control.LatestEstimate(), kInitialBitrate);
}

TEST(AimdRateControlTest, SetEstimateIncreaseBweInAlr) {
  ExplicitKeyValueConfig field_trials(
      "WebRTC-DontIncreaseDelayBasedBweInAlr/Enabled/");
  AimdRateControl aimd_rate_control(field_trials, /*send_side=*/true);
  constexpr DataRate kInitialBitrate = DataRate::BitsPerSec(123'000);
  aimd_rate_control.SetEstimate(kInitialBitrate, kInitialTime);
  aimd_rate_control.SetInApplicationLimitedRegion(true);
  ASSERT_EQ(aimd_rate_control.LatestEstimate(), kInitialBitrate);
  aimd_rate_control.SetEstimate(2 * kInitialBitrate, kInitialTime);
  EXPECT_EQ(aimd_rate_control.LatestEstimate(), 2 * kInitialBitrate);
}

TEST(AimdRateControlTest, SetEstimateUpperLimitedByNetworkEstimate) {
  AimdRateControl aimd_rate_control(ExplicitKeyValueConfig(""),
                                    /*send_side=*/true);
  NetworkStateEstimate network_estimate;
  network_estimate.link_capacity_upper = DataRate::BitsPerSec(400'000);
  aimd_rate_control.SetNetworkStateEstimate(network_estimate);
  aimd_rate_control.SetEstimate(DataRate::BitsPerSec(500'000), kInitialTime);
  EXPECT_EQ(aimd_rate_control.LatestEstimate(),
            network_estimate.link_capacity_upper);
}

TEST(AimdRateControlTest, SetEstimateLowerLimitedByNetworkEstimate) {
  AimdRateControl aimd_rate_control(ExplicitKeyValueConfig(""),
                                    /*send_side=*/true);
  NetworkStateEstimate network_estimate;
  network_estimate.link_capacity_lower = DataRate::BitsPerSec(400'000);
  aimd_rate_control.SetNetworkStateEstimate(network_estimate);
  aimd_rate_control.SetEstimate(DataRate::BitsPerSec(100'000), kInitialTime);
  // 0.85 is default backoff factor. (`beta_`)
  EXPECT_EQ(aimd_rate_control.LatestEstimate(),
            network_estimate.link_capacity_lower * 0.85);
}

TEST(AimdRateControlTest,
     SetEstimateIgnoredIfLowerThanNetworkEstimateAndCurrent) {
  AimdRateControl aimd_rate_control(ExplicitKeyValueConfig(""),
                                    /*send_side=*/true);
  aimd_rate_control.SetEstimate(DataRate::KilobitsPerSec(200), kInitialTime);
  ASSERT_EQ(aimd_rate_control.LatestEstimate().kbps(), 200);
  NetworkStateEstimate network_estimate;
  network_estimate.link_capacity_lower = DataRate::KilobitsPerSec(400);
  aimd_rate_control.SetNetworkStateEstimate(network_estimate);
  // Ignore the next SetEstimate, since the estimate is lower than 85% of
  // the network estimate.
  aimd_rate_control.SetEstimate(DataRate::KilobitsPerSec(100), kInitialTime);
  EXPECT_EQ(aimd_rate_control.LatestEstimate().kbps(), 200);
}

TEST(AimdRateControlTest, EstimateIncreaseWhileNotInAlr) {
  // Allow the estimate to increase as long as alr is not detected to ensure
  // tha BWE can not get stuck at a certain bitrate.
  ExplicitKeyValueConfig field_trials(
      "WebRTC-DontIncreaseDelayBasedBweInAlr/Enabled/");
  AimdRateControl aimd_rate_control(field_trials, /*send_side=*/true);
  Timestamp now = kInitialTime;
  constexpr DataRate kInitialBitrate = DataRate::BitsPerSec(123'000);
  aimd_rate_control.SetEstimate(kInitialBitrate, now);
  aimd_rate_control.SetInApplicationLimitedRegion(false);
  aimd_rate_control.Update({BandwidthUsage::kBwNormal, kInitialBitrate}, now);
  for (int i = 0; i < 100; ++i) {
    aimd_rate_control.Update({BandwidthUsage::kBwNormal, absl::nullopt}, now);
    now += TimeDelta::Millis(100);
  }
  EXPECT_GT(aimd_rate_control.LatestEstimate(), kInitialBitrate);
}

TEST(AimdRateControlTest, EstimateNotLimitedByNetworkEstimateIfDisabled) {
  ExplicitKeyValueConfig field_trials(
      "WebRTC-Bwe-EstimateBoundedIncrease/Disabled/");
  AimdRateControl aimd_rate_control(field_trials, /*send_side=*/true);
  Timestamp now = kInitialTime;
  constexpr DataRate kInitialBitrate = DataRate::BitsPerSec(123'000);
  aimd_rate_control.SetEstimate(kInitialBitrate, now);
  aimd_rate_control.SetInApplicationLimitedRegion(false);
  NetworkStateEstimate network_estimate;
  network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(150);
  aimd_rate_control.SetNetworkStateEstimate(network_estimate);

  for (int i = 0; i < 100; ++i) {
    aimd_rate_control.Update({BandwidthUsage::kBwNormal, absl::nullopt}, now);
    now += TimeDelta::Millis(100);
  }
  EXPECT_GT(aimd_rate_control.LatestEstimate(),
            network_estimate.link_capacity_upper);
}

}  // namespace webrtc
