|  | /* | 
|  | *  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 <cmath> | 
|  | #include <cstdint> | 
|  | #include <limits> | 
|  |  | 
|  | #include "rtc_base/checks.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 |