123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #include <AzCore/UnitTest/UnitTest.h>
- #include <AzCore/Math/MathUtils.h>
- namespace UnitTest
- {
- inline namespace FixedWidthIntegerTestUtils
- {
- // Utility to test each possible comparison between LeftType and RightType.
- // As there are 64 possible integer combinations for L and R to be tested,
- // this utility tests each of the 3 comparison permutations for each
- // combination as to avoid having 192 nearly identical test cases.
- template<typename LeftType, typename RightType>
- void ValidateSafeComparePermutations()
- {
- // gtest won't pick up on the permutations so print them out for sanity checking
- ColoredPrintf(COLOR_YELLOW, "%s\n", AZ_FUNCTION_SIGNATURE);
- // Case 1: lhs < rhs
- {
- constexpr LeftType lhs = (std::numeric_limits<LeftType>::lowest)();
- constexpr RightType rhs = (std::numeric_limits<RightType>::max)();
- // Expect lhs to be less than rhs
- static_assert(AZ::IntegralCompare::LessThan == AZ::SafeIntegralCompare(lhs, rhs));
- }
- // Case 2: lhs == rhs
- {
- // Given a lhs and rhs of 0
- constexpr LeftType lhs = 0;
- constexpr RightType rhs = 0;
- // Expect lhs and rhs to be equal
- static_assert(AZ::IntegralCompare::Equal == AZ::SafeIntegralCompare(lhs, rhs));
- }
- // Case 3: lhs > rhs
- {
- // Given a lhs of > 0 and a rhs of <= 0
- constexpr LeftType lhs = (std::numeric_limits<LeftType>::max)();
- constexpr RightType rhs = (std::numeric_limits<RightType>::lowest)();
- // Expect lhs to be greater than rhs
- static_assert(AZ::IntegralCompare::GreaterThan == AZ::SafeIntegralCompare(lhs, rhs));
- }
- }
- // The gtest equality macros do not safely handle integral comparisons between different
- // signs and widths so we will wrap around them with our own safe compare function in
- // order to test the clamped limits permutations below.
- template<typename LeftType, typename RightType>
- void SAFE_EXPECT_EQ(LeftType lhs, RightType rhs)
- {
- EXPECT_EQ(AZ::IntegralCompare::Equal, AZ::SafeIntegralCompare(lhs, rhs));
- }
- template<typename LeftType, typename RightType>
- void SAFE_EXPECT_LT(LeftType lhs, RightType rhs)
- {
- EXPECT_EQ(AZ::IntegralCompare::LessThan, AZ::SafeIntegralCompare(lhs, rhs));
- }
- template<typename LeftType, typename RightType>
- void SAFE_EXPECT_GT(LeftType lhs, RightType rhs)
- {
- EXPECT_EQ(AZ::IntegralCompare::GreaterThan, AZ::SafeIntegralCompare(lhs, rhs));
- }
- // Below are the different cases for integer combinations used by ValidateClampedLimits for each
- // possible combination of signedness and size of ValueType and ClampType.
- // The basic logic behind each case is the same (namely testing the
- // minimum and maximum clamped range of ValueType for a given ClampType) but the
- // conditions and expected results are subtley different from one another. As between
- // these partial specializations and the 64 explicit test cases covering the
- // ClampedIntegralLimits methods every single possible combination of ValueType,
- // ClampType and method call in ClampedIntegralLimits
- template <typename ClampType, typename ValueType>
- constexpr void TestSameSignednessAndEqualSize(ValueType lowestValue, ValueType maxValue)
- {
- // Given:
- // - ValueType is same as ClampType
- // Expect the natural numerical limits of ValueType to be equal to the clamped numerical limits of ValueType
- SAFE_EXPECT_EQ(AZ::ClampedIntegralLimits<ValueType, ClampType>::Min(), std::numeric_limits<ValueType>::lowest());
- SAFE_EXPECT_EQ(AZ::ClampedIntegralLimits<ValueType, ClampType>::Max(), (std::numeric_limits<ValueType>::max)());
- // Expect the natural numerical limits of ValueType to be equal to the natural numerical limits of ClampType
- SAFE_EXPECT_EQ(AZ::ClampedIntegralLimits<ValueType, ClampType>::Min(), std::numeric_limits<ClampType>::lowest());
- SAFE_EXPECT_EQ(AZ::ClampedIntegralLimits<ValueType, ClampType>::Max(), (std::numeric_limits<ClampType>::max)());
- // Clamp the value to the natural numerical range of ClampType
- auto[clampedValue1, overflow1] = AZ::ClampedIntegralLimits<ValueType, ClampType>::Clamp(lowestValue);
- // Expect the original and clamped value to be equal
- SAFE_EXPECT_EQ(clampedValue1, lowestValue);
- // Expect the value to not have overflowed ClampType's limits
- EXPECT_FALSE(overflow1);
- // Clamp the value to the natural numerical range of ClampType
- auto[clampedValue2, overflow2] = AZ::ClampedIntegralLimits<ValueType, ClampType>::Clamp(maxValue);
- // Expect the original and clamped value to be equal
- SAFE_EXPECT_EQ(clampedValue2, maxValue);
- // Expect the value to not have overflowed ClampType's limits
- EXPECT_FALSE(overflow2);
- }
- template <typename ClampType, typename ValueType>
- constexpr void TestValueTypeSignedAndValueTypeWider(ValueType lowestValue, ValueType maxValue)
- {
- // Given:
- // - ValueType is signed
- // - ClampType is signed or unsigned
- // - sizeof(ValueType) > sizeof(ClampType)
- // Expect the natural numerical range of ValueType to be larger the clamped numerical range of ValueType
- SAFE_EXPECT_GT(AZ::ClampedIntegralLimits<ValueType, ClampType>::Min(), std::numeric_limits<ValueType>::lowest());
- SAFE_EXPECT_LT(AZ::ClampedIntegralLimits<ValueType, ClampType>::Max(), (std::numeric_limits<ValueType>::max)());
- // Expect the clamped numerical limits of ValueType to equal the natural numerical limits of ClampType
- SAFE_EXPECT_EQ(AZ::ClampedIntegralLimits<ValueType, ClampType>::Min(), std::numeric_limits<ClampType>::lowest());
- SAFE_EXPECT_EQ(AZ::ClampedIntegralLimits<ValueType, ClampType>::Max(), (std::numeric_limits<ClampType>::max)());
- // Test natural minimum range of ValueType
- // Clamp the value to the natural numerical range of ClampType
- auto[clampedValue1, overflow1] = AZ::ClampedIntegralLimits<ValueType, ClampType>::Clamp(lowestValue);
- // Expect the clamped value to be greater than the original value
- SAFE_EXPECT_GT(clampedValue1, lowestValue);
- // Expect the clamped value to equal ClampType's natural min value
- SAFE_EXPECT_EQ(clampedValue1, std::numeric_limits<ClampType>::lowest());
- // Expect the value to have overflowed ClampType's limits
- EXPECT_TRUE(overflow1);
- // Test natural maximum range of ValueType
- // Clamp the value to the natural numerical range of ClampType
- auto[clampedValue2, overflow2] = AZ::ClampedIntegralLimits<ValueType, ClampType>::Clamp(maxValue);
- // Expect the clamped value to be less than the original value
- SAFE_EXPECT_LT(clampedValue2, maxValue);
- // Expect the clamped value to equal ClampType's natural min value
- SAFE_EXPECT_EQ(clampedValue2, (std::numeric_limits<ClampType>::max)());
- // Expect the value to have overflowed ClampType's limits
- EXPECT_TRUE(overflow2);
- }
- template <typename ClampType, typename ValueType>
- constexpr void TestClampTypeSignedAndClampTypeWider(ValueType lowestValue, ValueType maxValue)
- {
- // Given:
- // - ValueType is signed or unsigned
- // - ClampType is signed
- // - sizeof(ValueType) < sizeof(ClampType)
- // Expect the natural numerical limits of ValueType to be equal to the clamped numerical limits of ValueType
- SAFE_EXPECT_EQ(AZ::ClampedIntegralLimits<ValueType, ClampType>::Min(), std::numeric_limits<ValueType>::lowest());
- SAFE_EXPECT_EQ(AZ::ClampedIntegralLimits<ValueType, ClampType>::Max(), (std::numeric_limits<ValueType>::max)());
- // Expect the clamped numerical range of ValueType to be smaller than the natural numerical range of ClampType
- SAFE_EXPECT_GT(AZ::ClampedIntegralLimits<ValueType, ClampType>::Min(), std::numeric_limits<ClampType>::lowest());
- SAFE_EXPECT_LT(AZ::ClampedIntegralLimits<ValueType, ClampType>::Max(), (std::numeric_limits<ClampType>::max)());
- // Test natural minimum range of ValueType
- // Clamp the value to the natural numerical range of ClampType
- auto[clampedValue1, overflow1] = AZ::ClampedIntegralLimits<ValueType, ClampType>::Clamp(lowestValue);
- // Expect the original and clamped value to be equal
- SAFE_EXPECT_EQ(clampedValue1, lowestValue);
- // Expect the value to not have overflowed ClampType's limits
- EXPECT_FALSE(overflow1);
- // Test natural maximum range of ValueType
- // Clamp the value to the natural numerical range of ClampType
- auto[clampedValue2, overflow2] = AZ::ClampedIntegralLimits<ValueType, ClampType>::Clamp(maxValue);
- // Expect the original and clamped value to be equal
- SAFE_EXPECT_EQ(clampedValue2, maxValue);
- // Expect the value to not have overflowed ClampType's limits
- EXPECT_FALSE(overflow2);
- }
- template <typename ClampType, typename ValueType>
- constexpr void TestValueTypeSignedClampTypeUnsignedAndClampTypeEqualOrWider(ValueType lowestValue, ValueType maxValue)
- {
- // Given:
- // - ValueType is signed
- // - ClampType is unsigned
- // - sizeof(ValueType) <= sizeof(ClampType)
- // Expect the natural min value of ValueType to exceed the clamped numerical limits of ValueType
- SAFE_EXPECT_GT(AZ::ClampedIntegralLimits<ValueType, ClampType>::Min(), std::numeric_limits<ValueType>::lowest());
- // Expect the natural max value of ValueType to equal the clamped max value of ValueType
- SAFE_EXPECT_EQ(AZ::ClampedIntegralLimits<ValueType, ClampType>::Max(), (std::numeric_limits<ValueType>::max)());
- // Expect the clamped min value of ValueType to equal the natural min value of ClampType
- SAFE_EXPECT_EQ(AZ::ClampedIntegralLimits<ValueType, ClampType>::Min(), std::numeric_limits<ClampType>::lowest());
- // Expect the clamped max value of ValueType to be less than the natural max value of ClampType
- SAFE_EXPECT_LT(AZ::ClampedIntegralLimits<ValueType, ClampType>::Max(), (std::numeric_limits<ClampType>::max)());
- // Test natural minimum range of ValueType
- // Clamp the value to the natural numerical range of ClampType
- auto[clampedValue1, overflow1] = AZ::ClampedIntegralLimits<ValueType, ClampType>::Clamp(lowestValue);
- // Expect the clamped value to be greater than the original value
- SAFE_EXPECT_GT(clampedValue1, lowestValue);
- // Expect the clamped value to equal ClampType's natural min value
- SAFE_EXPECT_EQ(clampedValue1, std::numeric_limits<ClampType>::lowest());
- // Expect the value to have overflowed ClampType's limits
- EXPECT_TRUE(overflow1);
- // Test natural maximum range of ValueType
- // Clamp the value to the natural numerical range of ClampType
- auto[clampedValue2, overflow2] = AZ::ClampedIntegralLimits<ValueType, ClampType>::Clamp(maxValue);
- // Expect the original and clamped value to be equal
- SAFE_EXPECT_EQ(clampedValue2, maxValue);
- // Expect the value to not have overflowed ClampType's limits
- EXPECT_TRUE(!overflow2);
- }
- template <typename ClampType, typename ValueType>
- constexpr void TestValueTypeUnsignedClampTypeSignedAndValueTypeEqualOrWider(ValueType lowestValue, ValueType maxValue)
- {
- // Given:
- // - ValueType is unsigned
- // - ClampType is signed
- // - sizeof(ValueType) >= sizeof(ClampType)
- // Expect the natural min value of ValueType to equal the clamped min value of ValueType
- SAFE_EXPECT_EQ(AZ::ClampedIntegralLimits<ValueType, ClampType>::Min(), std::numeric_limits<ValueType>::lowest());
- // Expect the natural max value of ValueType to exceed the clamped max value of ValueType
- SAFE_EXPECT_LT(AZ::ClampedIntegralLimits<ValueType, ClampType>::Max(), (std::numeric_limits<ValueType>::max)());
- // Expect the clamped min value of ValueType to be greater than the natural min of ClampType
- SAFE_EXPECT_GT(AZ::ClampedIntegralLimits<ValueType, ClampType>::Min(), std::numeric_limits<ClampType>::lowest());
- // Expect the clamped max value of ValueType to equal the natural max of ClampType
- SAFE_EXPECT_EQ(AZ::ClampedIntegralLimits<ValueType, ClampType>::Max(), (std::numeric_limits<ClampType>::max)());
- // Test natural minimum range of ValueType
- // Clamp the value to the natural numerical range of ClampType
- auto[clampedValue1, overflow1] = AZ::ClampedIntegralLimits<ValueType, ClampType>::Clamp(lowestValue);
- // Expect the original and clamped value to be equal
- SAFE_EXPECT_EQ(clampedValue1, lowestValue);
- // Expect the value to not have overflowed ClampType's limits
- EXPECT_FALSE(overflow1);
- // Test natural maximum range of ValueType
- // Clamp the value to the natural numerical range of ClampType
- auto[clampedValue2, overflow2] = AZ::ClampedIntegralLimits<ValueType, ClampType>::Clamp(maxValue);
- // Expect the clamped value to be less than the original value
- SAFE_EXPECT_LT(clampedValue2, maxValue);
- // Expect the clamped value to equal ClampType's natural min value
- SAFE_EXPECT_EQ(clampedValue2, (std::numeric_limits<ClampType>::max)());
- // Expect the value to have overflowed ClampType's limits
- EXPECT_TRUE(overflow2);
- }
- template <typename ClampType, typename ValueType>
- constexpr void TestUnsignedAndValueTypeWider(ValueType lowestValue, ValueType maxValue)
- {
- // Given:
- // - ValueType is unsigned
- // - ClampType is unsigned
- // - sizeof(ValueType) > sizeof(ClampType)
- // Expect the natural min value of ValueType to equal the clamped min value of ValueType
- SAFE_EXPECT_EQ(AZ::ClampedIntegralLimits<ValueType, ClampType>::Min(), std::numeric_limits<ValueType>::lowest());
- // Expect the natural max value of ValueType to exceed the clamped max value of ValueType
- SAFE_EXPECT_LT(AZ::ClampedIntegralLimits<ValueType, ClampType>::Max(), (std::numeric_limits<ValueType>::max)());
- // Expect the clamped numerical limits of ValueType to equal the natural numerical limits of ClampType
- SAFE_EXPECT_EQ(AZ::ClampedIntegralLimits<ValueType, ClampType>::Min(), std::numeric_limits<ClampType>::lowest());
- SAFE_EXPECT_EQ(AZ::ClampedIntegralLimits<ValueType, ClampType>::Max(), (std::numeric_limits<ClampType>::max)());
- // Test natural minimum range of ValueType
- // Clamp the value to the natural numerical range of ClampType
- auto[clampedValue1, overflow1] = AZ::ClampedIntegralLimits<ValueType, ClampType>::Clamp(lowestValue);
- // Expect the original and clamped value to be equal
- SAFE_EXPECT_EQ(clampedValue1, lowestValue);
- // Expect the value to not have overflowed ClampType's limits
- EXPECT_FALSE(overflow1);
- // Test natural maximum range of ValueType
- // Clamp the value to the natural numerical range of ClampType
- auto[clampedValue2, overflow2] = AZ::ClampedIntegralLimits<ValueType, ClampType>::Clamp(maxValue);
- // Expect the clamped value to be less than the original value
- SAFE_EXPECT_LT(clampedValue2, maxValue);
- // Expect the clamped value to equal ClampType's natural min value
- SAFE_EXPECT_EQ(clampedValue2, (std::numeric_limits<ClampType>::max)());
- // Expect the value to have overflowed ClampType's limits
- EXPECT_TRUE(overflow2);
- }
- template <typename ClampType, typename ValueType>
- constexpr void TestUnsignedAndClampTypeWider(ValueType lowestValue, ValueType maxValue)
- {
- // Given:
- // - ValueType is unsigned
- // - ClampType is unsigned
- // - sizeof(ValueType) < sizeof(ClampType)
- // Expect the natural numerical limits of ValueType to be equal to the clamped numerical limits of ValueType
- SAFE_EXPECT_EQ(AZ::ClampedIntegralLimits<ValueType, ClampType>::Min(), std::numeric_limits<ValueType>::lowest());
- SAFE_EXPECT_EQ(AZ::ClampedIntegralLimits<ValueType, ClampType>::Max(), (std::numeric_limits<ValueType>::max)());
- // Expect the natural min value of ValueType to equal the clamped min value of ValueType
- SAFE_EXPECT_EQ(AZ::ClampedIntegralLimits<ValueType, ClampType>::Min(), std::numeric_limits<ClampType>::lowest());
- // Expect the natural max value of ValueType to exceed the clamped max value of ValueType
- SAFE_EXPECT_LT(AZ::ClampedIntegralLimits<ValueType, ClampType>::Max(), (std::numeric_limits<ClampType>::max)());
- // Test natural minimum range of ValueType
- // Clamp the value to the natural numerical range of ClampType
- auto[clampedValue1, overflow1] = AZ::ClampedIntegralLimits<ValueType, ClampType>::Clamp(lowestValue);
- // Expect the original and clamped value to be equal
- SAFE_EXPECT_EQ(clampedValue1, lowestValue);
- // Expect the value to not have overflowed ClampType's limits
- EXPECT_FALSE(overflow1);
- // Test natural maximum range of ValueType
- // Clamp the value to the natural numerical range of ClampType
- auto[clampedValue2, overflow2] = AZ::ClampedIntegralLimits<ValueType, ClampType>::Clamp(maxValue);
- // Expect the original and clamped value to be equal
- SAFE_EXPECT_EQ(clampedValue2, maxValue);
- // Expect the value to not have overflowed ClampType's limits
- EXPECT_FALSE(overflow2);
- }
- template <typename ValueType, typename ClampType, AZ::IntegralTypeDiff TypeDiff = AZ::IntegralTypeCompare<ValueType, ClampType>()>
- constexpr void IntegralTypeExpectationCompare(ValueType lowestValue, ValueType maxValue)
- {
- if constexpr (TypeDiff == AZ::IntegralTypeDiff::LSignedRSignedEqSize || TypeDiff == AZ::IntegralTypeDiff::LUnsignedRUnsignedEqSize)
- {
- TestSameSignednessAndEqualSize<ClampType>(lowestValue, maxValue);
- }
- else if constexpr (TypeDiff == AZ::IntegralTypeDiff::LSignedRSignedLWider || TypeDiff == AZ::IntegralTypeDiff::LSignedRUnsignedLWider)
- {
- TestValueTypeSignedAndValueTypeWider<ClampType>(lowestValue, maxValue);
- }
- else if constexpr (TypeDiff == AZ::IntegralTypeDiff::LSignedRSignedRWider || TypeDiff == AZ::IntegralTypeDiff::LUnsignedRSignedRWider)
- {
- TestClampTypeSignedAndClampTypeWider<ClampType>(lowestValue, maxValue);
- }
- else if constexpr (TypeDiff == AZ::IntegralTypeDiff::LSignedRUnsignedEqSize || TypeDiff == AZ::IntegralTypeDiff::LSignedRUnsignedRWider)
- {
- TestValueTypeSignedClampTypeUnsignedAndClampTypeEqualOrWider<ClampType>(lowestValue , maxValue);
- }
- else if constexpr (TypeDiff == AZ::IntegralTypeDiff::LUnsignedRSignedEqSize || TypeDiff == AZ::IntegralTypeDiff::LUnsignedRSignedLWider)
- {
- TestValueTypeUnsignedClampTypeSignedAndValueTypeEqualOrWider<ClampType>(lowestValue, maxValue);
- }
- else if constexpr(TypeDiff == AZ::IntegralTypeDiff::LUnsignedRUnsignedLWider)
- {
- TestUnsignedAndValueTypeWider<ClampType>(lowestValue, maxValue);
- }
- else if constexpr(TypeDiff == AZ::IntegralTypeDiff::LUnsignedRUnsignedRWider)
- {
- TestUnsignedAndClampTypeWider<ClampType>(lowestValue, maxValue);
- }
- }
- template<typename ValueType, typename ClampType>
- constexpr void ValidateClampedLimits()
- {
- // gtest won't pick up on the permutations so print them out for sanity checking
- ColoredPrintf(COLOR_YELLOW, "%s\n", AZ_FUNCTION_SIGNATURE);
- IntegralTypeExpectationCompare<ValueType, ClampType>(std::numeric_limits<ValueType>::lowest(), (std::numeric_limits<ValueType>::max)());
- }
- }
- template<typename ValueType>
- class IntegralTypeTestFixture
- : public ::testing::Test
- {
-
- };
- // gtest has a limit of 50 types, whereas we need 64 (one for each integral combination).
- // We will work around this by only submitting the 8 integral types to gtest but then
- // testing each type against all 8 integral types with a variadic template in order
- // to get the 64 integral type combinations we need for full test coverage.
- template <class... Types>
- struct TypesPack
- {
- template<typename T>
- static void ValidateSafeComparePermutations()
- {
- (FixedWidthIntegerTestUtils::ValidateSafeComparePermutations<T, Types>(), ...);
- }
- template<typename T>
- static void ValidateClampedLimits()
- {
- (FixedWidthIntegerTestUtils::ValidateClampedLimits<T, Types>(), ...);
- }
- };
- // The 8 intergral types that each typed test will test with to from each of the
- // 64 integral type combinations needed for full test coverage.
- using IntegralTypes = TypesPack
- <
- AZ::s8, AZ::u8, AZ::s16, AZ::u16, AZ::s32, AZ::u32, AZ::s64, AZ::u64
- >;
- // The 8 integral types that will form the typed tests.
- using IntegralTypeTestConfigs = ::testing::Types
- <
- AZ::s8, AZ::u8, AZ::s16, AZ::u16, AZ::s32, AZ::u32, AZ::s64, AZ::u64
- >;
- TYPED_TEST_SUITE(IntegralTypeTestFixture, IntegralTypeTestConfigs);
- ///////////////////////////////////////////////////////////////////////////
- TYPED_TEST(IntegralTypeTestFixture, SafeCompare)
- {
- IntegralTypes::ValidateSafeComparePermutations<TypeParam>();
- }
- TYPED_TEST(IntegralTypeTestFixture, ClampedLimits)
- {
- IntegralTypes::ValidateClampedLimits<TypeParam>();
- }
- }
|