/*
 *  Copyright (c) 2018 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 "call/rtp_bitrate_configurator.h"

#include <memory>

#include "test/gtest.h"

namespace webrtc {
using absl::nullopt;

class RtpBitrateConfiguratorTest : public ::testing::Test {
 public:
  RtpBitrateConfiguratorTest()
      : configurator_(new RtpBitrateConfigurator(BitrateConstraints())) {}
  std::unique_ptr<RtpBitrateConfigurator> configurator_;
  void UpdateConfigMatches(BitrateConstraints bitrate_config,
                           absl::optional<int> min_bitrate_bps,
                           absl::optional<int> start_bitrate_bps,
                           absl::optional<int> max_bitrate_bps) {
    absl::optional<BitrateConstraints> result =
        configurator_->UpdateWithSdpParameters(bitrate_config);
    EXPECT_TRUE(result.has_value());
    if (start_bitrate_bps.has_value())
      EXPECT_EQ(result->start_bitrate_bps, start_bitrate_bps);
    if (min_bitrate_bps.has_value())
      EXPECT_EQ(result->min_bitrate_bps, min_bitrate_bps);
    if (max_bitrate_bps.has_value())
      EXPECT_EQ(result->max_bitrate_bps, max_bitrate_bps);
  }

  void UpdateMaskMatches(BitrateSettings bitrate_mask,
                         absl::optional<int> min_bitrate_bps,
                         absl::optional<int> start_bitrate_bps,
                         absl::optional<int> max_bitrate_bps) {
    absl::optional<BitrateConstraints> result =
        configurator_->UpdateWithClientPreferences(bitrate_mask);
    EXPECT_TRUE(result.has_value());
    if (start_bitrate_bps.has_value())
      EXPECT_EQ(result->start_bitrate_bps, start_bitrate_bps);
    if (min_bitrate_bps.has_value())
      EXPECT_EQ(result->min_bitrate_bps, min_bitrate_bps);
    if (max_bitrate_bps.has_value())
      EXPECT_EQ(result->max_bitrate_bps, max_bitrate_bps);
  }
};

TEST_F(RtpBitrateConfiguratorTest, NewConfigWithValidConfigReturnsNewConfig) {
  BitrateConstraints bitrate_config;
  bitrate_config.min_bitrate_bps = 1;
  bitrate_config.start_bitrate_bps = 2;
  bitrate_config.max_bitrate_bps = 3;

  UpdateConfigMatches(bitrate_config, 1, 2, 3);
}

TEST_F(RtpBitrateConfiguratorTest, NewConfigWithDifferentMinReturnsNewConfig) {
  BitrateConstraints bitrate_config;
  bitrate_config.min_bitrate_bps = 10;
  bitrate_config.start_bitrate_bps = 20;
  bitrate_config.max_bitrate_bps = 30;
  configurator_.reset(new RtpBitrateConfigurator(bitrate_config));

  bitrate_config.min_bitrate_bps = 11;
  UpdateConfigMatches(bitrate_config, 11, -1, 30);
}

TEST_F(RtpBitrateConfiguratorTest,
       NewConfigWithDifferentStartReturnsNewConfig) {
  BitrateConstraints bitrate_config;
  bitrate_config.min_bitrate_bps = 10;
  bitrate_config.start_bitrate_bps = 20;
  bitrate_config.max_bitrate_bps = 30;
  configurator_.reset(new RtpBitrateConfigurator(bitrate_config));

  bitrate_config.start_bitrate_bps = 21;
  UpdateConfigMatches(bitrate_config, 10, 21, 30);
}

TEST_F(RtpBitrateConfiguratorTest, NewConfigWithDifferentMaxReturnsNewConfig) {
  BitrateConstraints bitrate_config;
  bitrate_config.min_bitrate_bps = 10;
  bitrate_config.start_bitrate_bps = 20;
  bitrate_config.max_bitrate_bps = 30;
  configurator_.reset(new RtpBitrateConfigurator(bitrate_config));

  bitrate_config.max_bitrate_bps = 31;
  UpdateConfigMatches(bitrate_config, 10, -1, 31);
}

TEST_F(RtpBitrateConfiguratorTest, NewConfigWithSameConfigElidesSecondCall) {
  BitrateConstraints bitrate_config;
  bitrate_config.min_bitrate_bps = 1;
  bitrate_config.start_bitrate_bps = 2;
  bitrate_config.max_bitrate_bps = 3;

  UpdateConfigMatches(bitrate_config, 1, 2, 3);
  EXPECT_FALSE(
      configurator_->UpdateWithSdpParameters(bitrate_config).has_value());
}

TEST_F(RtpBitrateConfiguratorTest,
       NewConfigWithSameMinMaxAndNegativeStartElidesSecondCall) {
  BitrateConstraints bitrate_config;
  bitrate_config.min_bitrate_bps = 1;
  bitrate_config.start_bitrate_bps = 2;
  bitrate_config.max_bitrate_bps = 3;

  UpdateConfigMatches(bitrate_config, 1, 2, 3);

  bitrate_config.start_bitrate_bps = -1;
  EXPECT_FALSE(
      configurator_->UpdateWithSdpParameters(bitrate_config).has_value());
}

TEST_F(RtpBitrateConfiguratorTest, BiggerMaskMinUsed) {
  BitrateSettings mask;
  mask.min_bitrate_bps = 1234;
  UpdateMaskMatches(mask, *mask.min_bitrate_bps, nullopt, nullopt);
}

TEST_F(RtpBitrateConfiguratorTest, BiggerConfigMinUsed) {
  BitrateSettings mask;
  mask.min_bitrate_bps = 1000;
  UpdateMaskMatches(mask, 1000, nullopt, nullopt);

  BitrateConstraints config;
  config.min_bitrate_bps = 1234;
  UpdateConfigMatches(config, 1234, nullopt, nullopt);
}

// The last call to set start should be used.
TEST_F(RtpBitrateConfiguratorTest, LatestStartMaskPreferred) {
  BitrateSettings mask;
  mask.start_bitrate_bps = 1300;
  UpdateMaskMatches(mask, nullopt, *mask.start_bitrate_bps, nullopt);

  BitrateConstraints bitrate_config;
  bitrate_config.start_bitrate_bps = 1200;

  UpdateConfigMatches(bitrate_config, nullopt, bitrate_config.start_bitrate_bps,
                      nullopt);
}

TEST_F(RtpBitrateConfiguratorTest, SmallerMaskMaxUsed) {
  BitrateConstraints bitrate_config;
  bitrate_config.max_bitrate_bps = bitrate_config.start_bitrate_bps + 2000;
  configurator_.reset(new RtpBitrateConfigurator(bitrate_config));

  BitrateSettings mask;
  mask.max_bitrate_bps = bitrate_config.start_bitrate_bps + 1000;

  UpdateMaskMatches(mask, nullopt, nullopt, *mask.max_bitrate_bps);
}

TEST_F(RtpBitrateConfiguratorTest, SmallerConfigMaxUsed) {
  BitrateConstraints bitrate_config;
  bitrate_config.max_bitrate_bps = bitrate_config.start_bitrate_bps + 1000;
  configurator_.reset(new RtpBitrateConfigurator(bitrate_config));

  BitrateSettings mask;
  mask.max_bitrate_bps = bitrate_config.start_bitrate_bps + 2000;

  // Expect no return because nothing changes
  EXPECT_FALSE(configurator_->UpdateWithClientPreferences(mask).has_value());
}

TEST_F(RtpBitrateConfiguratorTest, MaskStartLessThanConfigMinClamped) {
  BitrateConstraints bitrate_config;
  bitrate_config.min_bitrate_bps = 2000;
  configurator_.reset(new RtpBitrateConfigurator(bitrate_config));

  BitrateSettings mask;
  mask.start_bitrate_bps = 1000;
  UpdateMaskMatches(mask, 2000, 2000, nullopt);
}

TEST_F(RtpBitrateConfiguratorTest, MaskStartGreaterThanConfigMaxClamped) {
  BitrateConstraints bitrate_config;
  bitrate_config.start_bitrate_bps = 2000;
  configurator_.reset(new RtpBitrateConfigurator(bitrate_config));

  BitrateSettings mask;
  mask.max_bitrate_bps = 1000;

  UpdateMaskMatches(mask, nullopt, -1, 1000);
}

TEST_F(RtpBitrateConfiguratorTest, MaskMinGreaterThanConfigMaxClamped) {
  BitrateConstraints bitrate_config;
  bitrate_config.min_bitrate_bps = 2000;
  configurator_.reset(new RtpBitrateConfigurator(bitrate_config));

  BitrateSettings mask;
  mask.max_bitrate_bps = 1000;

  UpdateMaskMatches(mask, 1000, nullopt, 1000);
}

TEST_F(RtpBitrateConfiguratorTest, SettingMaskStartForcesUpdate) {
  BitrateSettings mask;
  mask.start_bitrate_bps = 1000;

  // Config should be returned twice with the same params since
  // start_bitrate_bps is set.
  UpdateMaskMatches(mask, nullopt, 1000, nullopt);
  UpdateMaskMatches(mask, nullopt, 1000, nullopt);
}

TEST_F(RtpBitrateConfiguratorTest, NewConfigWithNoChangesDoesNotCallNewConfig) {
  BitrateConstraints config1;
  config1.min_bitrate_bps = 0;
  config1.start_bitrate_bps = 1000;
  config1.max_bitrate_bps = -1;

  BitrateConstraints config2;
  config2.min_bitrate_bps = 0;
  config2.start_bitrate_bps = -1;
  config2.max_bitrate_bps = -1;

  // The second call should not return anything because it doesn't
  // change any values.
  UpdateConfigMatches(config1, 0, 1000, -1);
  EXPECT_FALSE(configurator_->UpdateWithSdpParameters(config2).has_value());
}

// If config changes the max, but not the effective max,
// new config shouldn't be returned, to avoid unnecessary encoder
// reconfigurations.
TEST_F(RtpBitrateConfiguratorTest,
       NewConfigNotReturnedWhenEffectiveMaxUnchanged) {
  BitrateConstraints config;
  config.min_bitrate_bps = 0;
  config.start_bitrate_bps = -1;
  config.max_bitrate_bps = 2000;
  UpdateConfigMatches(config, nullopt, nullopt, 2000);

  // Reduce effective max to 1000 with the mask.
  BitrateSettings mask;
  mask.max_bitrate_bps = 1000;
  UpdateMaskMatches(mask, nullopt, nullopt, 1000);

  // This leaves the effective max unchanged, so new config shouldn't be
  // returned again.
  config.max_bitrate_bps = 1000;
  EXPECT_FALSE(configurator_->UpdateWithSdpParameters(config).has_value());
}

// When the "start bitrate" mask is removed, new config shouldn't be returned
// again, since nothing's changing.
TEST_F(RtpBitrateConfiguratorTest, NewConfigNotReturnedWhenStartMaskRemoved) {
  BitrateSettings mask;
  mask.start_bitrate_bps = 1000;
  UpdateMaskMatches(mask, 0, 1000, -1);

  mask.start_bitrate_bps.reset();
  EXPECT_FALSE(configurator_->UpdateWithClientPreferences(mask).has_value());
}

// Test that if a new config is returned after BitrateSettings applies a
// "start" value, the new config won't return that start value a
// second time.
TEST_F(RtpBitrateConfiguratorTest, NewConfigAfterBitrateConfigMaskWithStart) {
  BitrateSettings mask;
  mask.start_bitrate_bps = 1000;
  UpdateMaskMatches(mask, 0, 1000, -1);

  BitrateConstraints config;
  config.min_bitrate_bps = 0;
  config.start_bitrate_bps = -1;
  config.max_bitrate_bps = 5000;
  // The start value isn't changing, so new config should be returned with
  // -1.
  UpdateConfigMatches(config, 0, -1, 5000);
}

TEST_F(RtpBitrateConfiguratorTest,
       NewConfigNotReturnedWhenClampedMinUnchanged) {
  BitrateConstraints bitrate_config;
  bitrate_config.start_bitrate_bps = 500;
  bitrate_config.max_bitrate_bps = 1000;
  configurator_.reset(new RtpBitrateConfigurator(bitrate_config));

  // Set min to 2000; it is clamped to the max (1000).
  BitrateSettings mask;
  mask.min_bitrate_bps = 2000;
  UpdateMaskMatches(mask, 1000, -1, 1000);

  // Set min to 3000; the clamped value stays the same so nothing happens.
  mask.min_bitrate_bps = 3000;
  EXPECT_FALSE(configurator_->UpdateWithClientPreferences(mask).has_value());
}
}  // namespace webrtc
