/*
 *  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/congestion_controller/goog_cc/probe_controller.h"

#include <memory>

#include "api/transport/field_trial_based_config.h"
#include "api/transport/network_types.h"
#include "api/units/data_rate.h"
#include "api/units/timestamp.h"
#include "logging/rtc_event_log/mock/mock_rtc_event_log.h"
#include "rtc_base/logging.h"
#include "system_wrappers/include/clock.h"
#include "test/field_trial.h"
#include "test/gmock.h"
#include "test/gtest.h"

using ::testing::_;
using ::testing::AtLeast;
using ::testing::Field;
using ::testing::Matcher;
using ::testing::NiceMock;
using ::testing::Return;

namespace webrtc {
namespace test {

namespace {

constexpr int kMinBitrateBps = 100;
constexpr int kStartBitrateBps = 300;
constexpr int kMaxBitrateBps = 10000;

constexpr int kExponentialProbingTimeoutMs = 5000;

constexpr int kAlrProbeInterval = 5000;
constexpr int kAlrEndedTimeoutMs = 3000;
constexpr int kBitrateDropTimeoutMs = 5000;
}  // namespace

class ProbeControllerTest : public ::testing::Test {
 protected:
  ProbeControllerTest() : clock_(100000000L) {
    probe_controller_.reset(
        new ProbeController(&field_trial_config_, &mock_rtc_event_log));
  }
  ~ProbeControllerTest() override {}

  std::vector<ProbeClusterConfig> SetNetworkAvailable(bool available) {
    NetworkAvailability msg;
    msg.at_time = Timestamp::Millis(NowMs());
    msg.network_available = available;
    return probe_controller_->OnNetworkAvailability(msg);
  }

  int64_t NowMs() { return clock_.TimeInMilliseconds(); }

  FieldTrialBasedConfig field_trial_config_;
  SimulatedClock clock_;
  NiceMock<MockRtcEventLog> mock_rtc_event_log;
  std::unique_ptr<ProbeController> probe_controller_;
};

TEST_F(ProbeControllerTest, InitiatesProbingAtStart) {
  auto probes = probe_controller_->SetBitrates(kMinBitrateBps, kStartBitrateBps,
                                               kMaxBitrateBps, NowMs());
  EXPECT_GE(probes.size(), 2u);
}

TEST_F(ProbeControllerTest, ProbeOnlyWhenNetworkIsUp) {
  SetNetworkAvailable(false);
  auto probes = probe_controller_->SetBitrates(kMinBitrateBps, kStartBitrateBps,
                                               kMaxBitrateBps, NowMs());
  EXPECT_EQ(probes.size(), 0u);
  probes = SetNetworkAvailable(true);
  EXPECT_GE(probes.size(), 2u);
}

TEST_F(ProbeControllerTest, InitiatesProbingOnMaxBitrateIncrease) {
  auto probes = probe_controller_->SetBitrates(kMinBitrateBps, kStartBitrateBps,
                                               kMaxBitrateBps, NowMs());
  // Long enough to time out exponential probing.
  clock_.AdvanceTimeMilliseconds(kExponentialProbingTimeoutMs);
  probes = probe_controller_->SetEstimatedBitrate(kStartBitrateBps, NowMs());
  probes = probe_controller_->Process(NowMs());
  probes = probe_controller_->SetBitrates(kMinBitrateBps, kStartBitrateBps,
                                          kMaxBitrateBps + 100, NowMs());
  EXPECT_EQ(probes.size(), 1u);
  EXPECT_EQ(probes[0].target_data_rate.bps(), kMaxBitrateBps + 100);
}

TEST_F(ProbeControllerTest, ProbesOnMaxBitrateIncreaseOnlyWhenInAlr) {
  probe_controller_.reset(
      new ProbeController(&field_trial_config_, &mock_rtc_event_log));
  auto probes = probe_controller_->SetBitrates(kMinBitrateBps, kStartBitrateBps,
                                               kMaxBitrateBps, NowMs());
  probes = probe_controller_->SetEstimatedBitrate(kMaxBitrateBps - 1, NowMs());

  // Wait long enough to time out exponential probing.
  clock_.AdvanceTimeMilliseconds(kExponentialProbingTimeoutMs);
  probes = probe_controller_->Process(NowMs());
  EXPECT_EQ(probes.size(), 0u);

  // Probe when in alr.
  probe_controller_->SetAlrStartTimeMs(clock_.TimeInMilliseconds());
  probes = probe_controller_->OnMaxTotalAllocatedBitrate(kMaxBitrateBps + 1,
                                                         NowMs());
  EXPECT_EQ(probes.size(), 2u);

  // Do not probe when not in alr.
  probe_controller_->SetAlrStartTimeMs(absl::nullopt);
  probes = probe_controller_->OnMaxTotalAllocatedBitrate(kMaxBitrateBps + 2,
                                                         NowMs());
  EXPECT_TRUE(probes.empty());
}

TEST_F(ProbeControllerTest, InitiatesProbingOnMaxBitrateIncreaseAtMaxBitrate) {
  auto probes = probe_controller_->SetBitrates(kMinBitrateBps, kStartBitrateBps,
                                               kMaxBitrateBps, NowMs());
  // Long enough to time out exponential probing.
  clock_.AdvanceTimeMilliseconds(kExponentialProbingTimeoutMs);
  probes = probe_controller_->SetEstimatedBitrate(kStartBitrateBps, NowMs());
  probes = probe_controller_->Process(NowMs());
  probes = probe_controller_->SetEstimatedBitrate(kMaxBitrateBps, NowMs());
  probes = probe_controller_->SetBitrates(kMinBitrateBps, kStartBitrateBps,
                                          kMaxBitrateBps + 100, NowMs());
  EXPECT_EQ(probes.size(), 1u);
  EXPECT_EQ(probes[0].target_data_rate.bps(), kMaxBitrateBps + 100);
}

TEST_F(ProbeControllerTest, TestExponentialProbing) {
  auto probes = probe_controller_->SetBitrates(kMinBitrateBps, kStartBitrateBps,
                                               kMaxBitrateBps, NowMs());

  // Repeated probe should only be sent when estimated bitrate climbs above
  // 0.7 * 6 * kStartBitrateBps = 1260.
  probes = probe_controller_->SetEstimatedBitrate(1000, NowMs());
  EXPECT_EQ(probes.size(), 0u);

  probes = probe_controller_->SetEstimatedBitrate(1800, NowMs());
  EXPECT_EQ(probes.size(), 1u);
  EXPECT_EQ(probes[0].target_data_rate.bps(), 2 * 1800);
}

TEST_F(ProbeControllerTest, TestExponentialProbingTimeout) {
  auto probes = probe_controller_->SetBitrates(kMinBitrateBps, kStartBitrateBps,
                                               kMaxBitrateBps, NowMs());
  // Advance far enough to cause a time out in waiting for probing result.
  clock_.AdvanceTimeMilliseconds(kExponentialProbingTimeoutMs);
  probes = probe_controller_->Process(NowMs());

  probes = probe_controller_->SetEstimatedBitrate(1800, NowMs());
  EXPECT_EQ(probes.size(), 0u);
}

TEST_F(ProbeControllerTest, RequestProbeInAlr) {
  auto probes = probe_controller_->SetBitrates(kMinBitrateBps, kStartBitrateBps,
                                               kMaxBitrateBps, NowMs());
  EXPECT_GE(probes.size(), 2u);
  probes = probe_controller_->SetEstimatedBitrate(500, NowMs());

  probe_controller_->SetAlrStartTimeMs(clock_.TimeInMilliseconds());
  clock_.AdvanceTimeMilliseconds(kAlrProbeInterval + 1);
  probes = probe_controller_->Process(NowMs());
  probes = probe_controller_->SetEstimatedBitrate(250, NowMs());
  probes = probe_controller_->RequestProbe(NowMs());

  EXPECT_EQ(probes.size(), 1u);
  EXPECT_EQ(probes[0].target_data_rate.bps(), 0.85 * 500);
}

TEST_F(ProbeControllerTest, RequestProbeWhenAlrEndedRecently) {
  auto probes = probe_controller_->SetBitrates(kMinBitrateBps, kStartBitrateBps,
                                               kMaxBitrateBps, NowMs());
  EXPECT_EQ(probes.size(), 2u);
  probes = probe_controller_->SetEstimatedBitrate(500, NowMs());

  probe_controller_->SetAlrStartTimeMs(absl::nullopt);
  clock_.AdvanceTimeMilliseconds(kAlrProbeInterval + 1);
  probes = probe_controller_->Process(NowMs());
  probes = probe_controller_->SetEstimatedBitrate(250, NowMs());
  probe_controller_->SetAlrEndedTimeMs(clock_.TimeInMilliseconds());
  clock_.AdvanceTimeMilliseconds(kAlrEndedTimeoutMs - 1);
  probes = probe_controller_->RequestProbe(NowMs());

  EXPECT_EQ(probes.size(), 1u);
  EXPECT_EQ(probes[0].target_data_rate.bps(), 0.85 * 500);
}

TEST_F(ProbeControllerTest, RequestProbeWhenAlrNotEndedRecently) {
  auto probes = probe_controller_->SetBitrates(kMinBitrateBps, kStartBitrateBps,
                                               kMaxBitrateBps, NowMs());
  EXPECT_EQ(probes.size(), 2u);
  probes = probe_controller_->SetEstimatedBitrate(500, NowMs());

  probe_controller_->SetAlrStartTimeMs(absl::nullopt);
  clock_.AdvanceTimeMilliseconds(kAlrProbeInterval + 1);
  probes = probe_controller_->Process(NowMs());
  probes = probe_controller_->SetEstimatedBitrate(250, NowMs());
  probe_controller_->SetAlrEndedTimeMs(clock_.TimeInMilliseconds());
  clock_.AdvanceTimeMilliseconds(kAlrEndedTimeoutMs + 1);
  probes = probe_controller_->RequestProbe(NowMs());
  EXPECT_EQ(probes.size(), 0u);
}

TEST_F(ProbeControllerTest, RequestProbeWhenBweDropNotRecent) {
  auto probes = probe_controller_->SetBitrates(kMinBitrateBps, kStartBitrateBps,
                                               kMaxBitrateBps, NowMs());
  EXPECT_EQ(probes.size(), 2u);
  probes = probe_controller_->SetEstimatedBitrate(500, NowMs());

  probe_controller_->SetAlrStartTimeMs(clock_.TimeInMilliseconds());
  clock_.AdvanceTimeMilliseconds(kAlrProbeInterval + 1);
  probes = probe_controller_->Process(NowMs());
  probes = probe_controller_->SetEstimatedBitrate(250, NowMs());
  clock_.AdvanceTimeMilliseconds(kBitrateDropTimeoutMs + 1);
  probes = probe_controller_->RequestProbe(NowMs());
  EXPECT_EQ(probes.size(), 0u);
}

TEST_F(ProbeControllerTest, PeriodicProbing) {
  probe_controller_->EnablePeriodicAlrProbing(true);
  auto probes = probe_controller_->SetBitrates(kMinBitrateBps, kStartBitrateBps,
                                               kMaxBitrateBps, NowMs());
  EXPECT_EQ(probes.size(), 2u);
  probes = probe_controller_->SetEstimatedBitrate(500, NowMs());

  int64_t start_time = clock_.TimeInMilliseconds();

  // Expect the controller to send a new probe after 5s has passed.
  probe_controller_->SetAlrStartTimeMs(start_time);
  clock_.AdvanceTimeMilliseconds(5000);
  probes = probe_controller_->Process(NowMs());
  EXPECT_EQ(probes.size(), 1u);
  EXPECT_EQ(probes[0].target_data_rate.bps(), 1000);

  probes = probe_controller_->SetEstimatedBitrate(500, NowMs());

  // The following probe should be sent at 10s into ALR.
  probe_controller_->SetAlrStartTimeMs(start_time);
  clock_.AdvanceTimeMilliseconds(4000);
  probes = probe_controller_->Process(NowMs());
  probes = probe_controller_->SetEstimatedBitrate(500, NowMs());
  EXPECT_EQ(probes.size(), 0u);

  probe_controller_->SetAlrStartTimeMs(start_time);
  clock_.AdvanceTimeMilliseconds(1000);
  probes = probe_controller_->Process(NowMs());
  EXPECT_EQ(probes.size(), 1u);
  probes = probe_controller_->SetEstimatedBitrate(500, NowMs());
  EXPECT_EQ(probes.size(), 0u);
}

TEST_F(ProbeControllerTest, PeriodicProbingAfterReset) {
  probe_controller_.reset(
      new ProbeController(&field_trial_config_, &mock_rtc_event_log));
  int64_t alr_start_time = clock_.TimeInMilliseconds();

  probe_controller_->SetAlrStartTimeMs(alr_start_time);
  probe_controller_->EnablePeriodicAlrProbing(true);
  auto probes = probe_controller_->SetBitrates(kMinBitrateBps, kStartBitrateBps,
                                               kMaxBitrateBps, NowMs());
  probe_controller_->Reset(NowMs());

  clock_.AdvanceTimeMilliseconds(10000);
  probes = probe_controller_->Process(NowMs());
  // Since bitrates are not yet set, no probe is sent event though we are in ALR
  // mode.
  EXPECT_EQ(probes.size(), 0u);

  probes = probe_controller_->SetBitrates(kMinBitrateBps, kStartBitrateBps,
                                          kMaxBitrateBps, NowMs());
  EXPECT_EQ(probes.size(), 2u);

  // Make sure we use |kStartBitrateBps| as the estimated bitrate
  // until SetEstimatedBitrate is called with an updated estimate.
  clock_.AdvanceTimeMilliseconds(10000);
  probes = probe_controller_->Process(NowMs());
  EXPECT_EQ(probes.size(), 1u);
  EXPECT_EQ(probes[0].target_data_rate.bps(), kStartBitrateBps * 2);
}

TEST_F(ProbeControllerTest, TestExponentialProbingOverflow) {
  const int64_t kMbpsMultiplier = 1000000;
  auto probes = probe_controller_->SetBitrates(
      kMinBitrateBps, 10 * kMbpsMultiplier, 100 * kMbpsMultiplier, NowMs());
  // Verify that probe bitrate is capped at the specified max bitrate.
  probes =
      probe_controller_->SetEstimatedBitrate(60 * kMbpsMultiplier, NowMs());
  EXPECT_EQ(probes.size(), 1u);
  EXPECT_EQ(probes[0].target_data_rate.bps(), 100 * kMbpsMultiplier);
  // Verify that repeated probes aren't sent.
  probes =
      probe_controller_->SetEstimatedBitrate(100 * kMbpsMultiplier, NowMs());
  EXPECT_EQ(probes.size(), 0u);
}

TEST_F(ProbeControllerTest, TestAllocatedBitrateCap) {
  const int64_t kMbpsMultiplier = 1000000;
  const int64_t kMaxBitrateBps = 100 * kMbpsMultiplier;
  auto probes = probe_controller_->SetBitrates(
      kMinBitrateBps, 10 * kMbpsMultiplier, kMaxBitrateBps, NowMs());

  // Configure ALR for periodic probing.
  probe_controller_->EnablePeriodicAlrProbing(true);
  int64_t alr_start_time = clock_.TimeInMilliseconds();
  probe_controller_->SetAlrStartTimeMs(alr_start_time);

  int64_t estimated_bitrate_bps = kMaxBitrateBps / 10;
  probes =
      probe_controller_->SetEstimatedBitrate(estimated_bitrate_bps, NowMs());

  // Set a max allocated bitrate below the current estimate.
  int64_t max_allocated_bps = estimated_bitrate_bps - 1 * kMbpsMultiplier;
  probes =
      probe_controller_->OnMaxTotalAllocatedBitrate(max_allocated_bps, NowMs());
  EXPECT_TRUE(probes.empty());  // No probe since lower than current max.

  // Probes such as ALR capped at 2x the max allocation limit.
  clock_.AdvanceTimeMilliseconds(5000);
  probes = probe_controller_->Process(NowMs());
  EXPECT_EQ(probes.size(), 1u);
  EXPECT_EQ(probes[0].target_data_rate.bps(), 2 * max_allocated_bps);

  // Remove allocation limit.
  EXPECT_TRUE(
      probe_controller_->OnMaxTotalAllocatedBitrate(0, NowMs()).empty());
  clock_.AdvanceTimeMilliseconds(5000);
  probes = probe_controller_->Process(NowMs());
  EXPECT_EQ(probes.size(), 1u);
  EXPECT_EQ(probes[0].target_data_rate.bps(), estimated_bitrate_bps * 2);
}

TEST_F(ProbeControllerTest, ConfigurableProbingFieldTrial) {
  test::ScopedFieldTrials trials(
      "WebRTC-Bwe-ProbingConfiguration/"
      "p1:2,p2:5,step_size:3,further_probe_threshold:0.8,"
      "alloc_p1:2,alloc_p2/");
  probe_controller_.reset(
      new ProbeController(&field_trial_config_, &mock_rtc_event_log));
  auto probes = probe_controller_->SetBitrates(kMinBitrateBps, kStartBitrateBps,
                                               5000000, NowMs());
  EXPECT_EQ(probes.size(), 2u);
  EXPECT_EQ(probes[0].target_data_rate.bps(), 600);
  EXPECT_EQ(probes[1].target_data_rate.bps(), 1500);

  // Repeated probe should only be sent when estimated bitrate climbs above
  // 0.8 * 5 * kStartBitrateBps = 1200.
  probes = probe_controller_->SetEstimatedBitrate(1100, NowMs());
  EXPECT_EQ(probes.size(), 0u);

  probes = probe_controller_->SetEstimatedBitrate(1250, NowMs());
  EXPECT_EQ(probes.size(), 1u);
  EXPECT_EQ(probes[0].target_data_rate.bps(), 3 * 1250);

  clock_.AdvanceTimeMilliseconds(5000);
  probes = probe_controller_->Process(NowMs());

  probe_controller_->SetAlrStartTimeMs(NowMs());
  probes = probe_controller_->OnMaxTotalAllocatedBitrate(200000, NowMs());
  EXPECT_EQ(probes.size(), 1u);
  EXPECT_EQ(probes[0].target_data_rate.bps(), 400000);
}

}  // namespace test
}  // namespace webrtc
