blob: 6003f4b754869c4a7e3f518585b57ca33fd5bdaf [file] [log] [blame]
/*
* 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 "rtc_base/units/unit_base.h"
#include "test/gtest.h"
namespace webrtc {
namespace {
class TestUnit final : public rtc_units_impl::RelativeUnit<TestUnit> {
public:
TestUnit() = delete;
using UnitBase::FromValue;
using UnitBase::ToValue;
using UnitBase::ToValueOr;
template <typename T>
static constexpr TestUnit FromKilo(T kilo) {
return FromFraction(1000, kilo);
}
template <typename T = int64_t>
T ToKilo() const {
return UnitBase::ToFraction<1000, T>();
}
constexpr int64_t ToKiloOr(int64_t fallback) const {
return UnitBase::ToFractionOr<1000>(fallback);
}
template <typename T>
constexpr T ToMilli() const {
return UnitBase::ToMultiple<1000, T>();
}
private:
friend class rtc_units_impl::UnitBase<TestUnit>;
static constexpr bool one_sided = false;
using RelativeUnit<TestUnit>::RelativeUnit;
};
constexpr TestUnit TestUnitAddKilo(TestUnit value, int add_kilo) {
value += TestUnit::FromKilo(add_kilo);
return value;
}
} // namespace
namespace test {
TEST(UnitBaseTest, ConstExpr) {
constexpr int64_t kValue = -12345;
constexpr TestUnit kTestUnitZero = TestUnit::Zero();
constexpr TestUnit kTestUnitPlusInf = TestUnit::PlusInfinity();
constexpr TestUnit kTestUnitMinusInf = TestUnit::MinusInfinity();
static_assert(kTestUnitZero.IsZero(), "");
static_assert(kTestUnitPlusInf.IsPlusInfinity(), "");
static_assert(kTestUnitMinusInf.IsMinusInfinity(), "");
static_assert(kTestUnitPlusInf.ToKiloOr(-1) == -1, "");
// Check FromValue is constexpr for floats.
static_assert(TestUnit::FromValue(0.0).IsZero());
static_assert(TestUnit::FromValue(INFINITY).IsPlusInfinity());
static_assert(TestUnit::FromValue(-INFINITY).IsMinusInfinity());
static_assert(TestUnit::FromValue(250.0) == TestUnit::FromValue(250));
static_assert(TestUnit::FromValue(-250.0) == TestUnit::FromValue(-250));
static_assert(kTestUnitPlusInf > kTestUnitZero, "");
constexpr TestUnit kTestUnitKilo = TestUnit::FromKilo(kValue);
constexpr TestUnit kTestUnitValue = TestUnit::FromValue(kValue);
static_assert(kTestUnitKilo.ToKiloOr(0) == kValue, "");
static_assert(kTestUnitValue.ToValueOr(0) == kValue, "");
static_assert(TestUnitAddKilo(kTestUnitValue, 2).ToValue() == kValue + 2000,
"");
static_assert(TestUnit::FromValue(500) / 2 == TestUnit::FromValue(250));
static_assert(TestUnit::FromValue(500.0) / 2 == TestUnit::FromValue(250.0));
}
TEST(UnitBaseTest, GetBackSameValues) {
const int64_t kValue = 499;
for (int sign = -1; sign <= 1; ++sign) {
int64_t value = kValue * sign;
EXPECT_EQ(TestUnit::FromKilo(value).ToKilo(), value);
EXPECT_EQ(TestUnit::FromValue(value).ToValue<int64_t>(), value);
}
EXPECT_EQ(TestUnit::Zero().ToValue<int64_t>(), 0);
}
TEST(UnitBaseTest, GetDifferentPrefix) {
const int64_t kValue = 3000000;
EXPECT_EQ(TestUnit::FromValue(kValue).ToKilo(), kValue / 1000);
EXPECT_EQ(TestUnit::FromKilo(kValue).ToValue<int64_t>(), kValue * 1000);
}
TEST(UnitBaseTest, IdentityChecks) {
const int64_t kValue = 3000;
EXPECT_TRUE(TestUnit::Zero().IsZero());
EXPECT_FALSE(TestUnit::FromKilo(kValue).IsZero());
EXPECT_TRUE(TestUnit::PlusInfinity().IsInfinite());
EXPECT_TRUE(TestUnit::MinusInfinity().IsInfinite());
EXPECT_FALSE(TestUnit::Zero().IsInfinite());
EXPECT_FALSE(TestUnit::FromKilo(-kValue).IsInfinite());
EXPECT_FALSE(TestUnit::FromKilo(kValue).IsInfinite());
EXPECT_FALSE(TestUnit::PlusInfinity().IsFinite());
EXPECT_FALSE(TestUnit::MinusInfinity().IsFinite());
EXPECT_TRUE(TestUnit::FromKilo(-kValue).IsFinite());
EXPECT_TRUE(TestUnit::FromKilo(kValue).IsFinite());
EXPECT_TRUE(TestUnit::Zero().IsFinite());
EXPECT_TRUE(TestUnit::PlusInfinity().IsPlusInfinity());
EXPECT_FALSE(TestUnit::MinusInfinity().IsPlusInfinity());
EXPECT_TRUE(TestUnit::MinusInfinity().IsMinusInfinity());
EXPECT_FALSE(TestUnit::PlusInfinity().IsMinusInfinity());
}
TEST(UnitBaseTest, ComparisonOperators) {
const int64_t kSmall = 450;
const int64_t kLarge = 451;
const TestUnit small = TestUnit::FromKilo(kSmall);
const TestUnit large = TestUnit::FromKilo(kLarge);
EXPECT_EQ(TestUnit::Zero(), TestUnit::FromKilo(0));
EXPECT_EQ(TestUnit::PlusInfinity(), TestUnit::PlusInfinity());
EXPECT_EQ(small, TestUnit::FromKilo(kSmall));
EXPECT_LE(small, TestUnit::FromKilo(kSmall));
EXPECT_GE(small, TestUnit::FromKilo(kSmall));
EXPECT_NE(small, TestUnit::FromKilo(kLarge));
EXPECT_LE(small, TestUnit::FromKilo(kLarge));
EXPECT_LT(small, TestUnit::FromKilo(kLarge));
EXPECT_GE(large, TestUnit::FromKilo(kSmall));
EXPECT_GT(large, TestUnit::FromKilo(kSmall));
EXPECT_LT(TestUnit::Zero(), small);
EXPECT_GT(TestUnit::Zero(), TestUnit::FromKilo(-kSmall));
EXPECT_GT(TestUnit::Zero(), TestUnit::FromKilo(-kSmall));
EXPECT_GT(TestUnit::PlusInfinity(), large);
EXPECT_LT(TestUnit::MinusInfinity(), TestUnit::Zero());
}
TEST(UnitBaseTest, Clamping) {
const TestUnit upper = TestUnit::FromKilo(800);
const TestUnit lower = TestUnit::FromKilo(100);
const TestUnit under = TestUnit::FromKilo(100);
const TestUnit inside = TestUnit::FromKilo(500);
const TestUnit over = TestUnit::FromKilo(1000);
EXPECT_EQ(under.Clamped(lower, upper), lower);
EXPECT_EQ(inside.Clamped(lower, upper), inside);
EXPECT_EQ(over.Clamped(lower, upper), upper);
TestUnit mutable_delta = lower;
mutable_delta.Clamp(lower, upper);
EXPECT_EQ(mutable_delta, lower);
mutable_delta = inside;
mutable_delta.Clamp(lower, upper);
EXPECT_EQ(mutable_delta, inside);
mutable_delta = over;
mutable_delta.Clamp(lower, upper);
EXPECT_EQ(mutable_delta, upper);
}
TEST(UnitBaseTest, CanBeInititializedFromLargeInt) {
const int kMaxInt = std::numeric_limits<int>::max();
EXPECT_EQ(TestUnit::FromKilo(kMaxInt).ToValue<int64_t>(),
static_cast<int64_t>(kMaxInt) * 1000);
}
TEST(UnitBaseTest, ConvertsToAndFromDouble) {
const int64_t kValue = 17017;
const double kMilliDouble = kValue * 1e3;
const double kValueDouble = kValue;
const double kKiloDouble = kValue * 1e-3;
EXPECT_EQ(TestUnit::FromValue(kValue).ToKilo<double>(), kKiloDouble);
EXPECT_EQ(TestUnit::FromKilo(kKiloDouble).ToValue<int64_t>(), kValue);
EXPECT_EQ(TestUnit::FromValue(kValue).ToValue<double>(), kValueDouble);
EXPECT_EQ(TestUnit::FromValue(kValueDouble).ToValue<int64_t>(), kValue);
EXPECT_NEAR(TestUnit::FromValue(kValue).ToMilli<double>(), kMilliDouble, 1);
const double kPlusInfinity = std::numeric_limits<double>::infinity();
const double kMinusInfinity = -kPlusInfinity;
EXPECT_EQ(TestUnit::PlusInfinity().ToKilo<double>(), kPlusInfinity);
EXPECT_EQ(TestUnit::MinusInfinity().ToKilo<double>(), kMinusInfinity);
EXPECT_EQ(TestUnit::PlusInfinity().ToValue<double>(), kPlusInfinity);
EXPECT_EQ(TestUnit::MinusInfinity().ToValue<double>(), kMinusInfinity);
EXPECT_EQ(TestUnit::PlusInfinity().ToMilli<double>(), kPlusInfinity);
EXPECT_EQ(TestUnit::MinusInfinity().ToMilli<double>(), kMinusInfinity);
EXPECT_TRUE(TestUnit::FromKilo(kPlusInfinity).IsPlusInfinity());
EXPECT_TRUE(TestUnit::FromKilo(kMinusInfinity).IsMinusInfinity());
EXPECT_TRUE(TestUnit::FromValue(kPlusInfinity).IsPlusInfinity());
EXPECT_TRUE(TestUnit::FromValue(kMinusInfinity).IsMinusInfinity());
}
TEST(UnitBaseTest, MathOperations) {
const int64_t kValueA = 267;
const int64_t kValueB = 450;
const TestUnit delta_a = TestUnit::FromKilo(kValueA);
const TestUnit delta_b = TestUnit::FromKilo(kValueB);
EXPECT_EQ((delta_a + delta_b).ToKilo(), kValueA + kValueB);
EXPECT_EQ((delta_a - delta_b).ToKilo(), kValueA - kValueB);
const int32_t kInt32Value = 123;
const double kFloatValue = 123.0;
EXPECT_EQ((TestUnit::FromValue(kValueA) * kValueB).ToValue<int64_t>(),
kValueA * kValueB);
EXPECT_EQ((TestUnit::FromValue(kValueA) * kInt32Value).ToValue<int64_t>(),
kValueA * kInt32Value);
EXPECT_EQ((TestUnit::FromValue(kValueA) * kFloatValue).ToValue<int64_t>(),
kValueA * kFloatValue);
EXPECT_EQ((delta_b / 10).ToKilo(), kValueB / 10);
EXPECT_EQ(delta_b / delta_a, static_cast<double>(kValueB) / kValueA);
TestUnit mutable_delta = TestUnit::FromKilo(kValueA);
mutable_delta += TestUnit::FromKilo(kValueB);
EXPECT_EQ(mutable_delta, TestUnit::FromKilo(kValueA + kValueB));
mutable_delta -= TestUnit::FromKilo(kValueB);
EXPECT_EQ(mutable_delta, TestUnit::FromKilo(kValueA));
// Division by an int rounds towards zero to follow regular int division.
EXPECT_EQ(TestUnit::FromValue(789) / 10, TestUnit::FromValue(78));
EXPECT_EQ(TestUnit::FromValue(-789) / 10, TestUnit::FromValue(-78));
}
#if GTEST_HAS_DEATH_TEST && RTC_DCHECK_IS_ON && !defined(WEBRTC_ANDROID)
TEST(UnitBaseTest, CrashesWhenCreatedFromNan) {
EXPECT_DEATH(TestUnit::FromValue(NAN), "");
EXPECT_DEATH(TestUnit::FromValue(0.0 / 0.0), "");
EXPECT_DEATH(TestUnit::FromValue(INFINITY - INFINITY), "");
}
#endif
TEST(UnitBaseTest, InfinityOperations) {
const int64_t kValue = 267;
const TestUnit finite = TestUnit::FromKilo(kValue);
EXPECT_TRUE((TestUnit::PlusInfinity() + finite).IsPlusInfinity());
EXPECT_TRUE((TestUnit::PlusInfinity() - finite).IsPlusInfinity());
EXPECT_TRUE((finite + TestUnit::PlusInfinity()).IsPlusInfinity());
EXPECT_TRUE((finite - TestUnit::MinusInfinity()).IsPlusInfinity());
EXPECT_TRUE((TestUnit::MinusInfinity() + finite).IsMinusInfinity());
EXPECT_TRUE((TestUnit::MinusInfinity() - finite).IsMinusInfinity());
EXPECT_TRUE((finite + TestUnit::MinusInfinity()).IsMinusInfinity());
EXPECT_TRUE((finite - TestUnit::PlusInfinity()).IsMinusInfinity());
}
TEST(UnitBaseTest, UnaryMinus) {
const int64_t kValue = 1337;
const TestUnit unit = TestUnit::FromValue(kValue);
EXPECT_EQ(-unit.ToValue(), -kValue);
// Check infinity.
EXPECT_EQ(-TestUnit::PlusInfinity(), TestUnit::MinusInfinity());
EXPECT_EQ(-TestUnit::MinusInfinity(), TestUnit::PlusInfinity());
}
} // namespace test
} // namespace webrtc