// Copyright Epic Games, Inc. All Rights Reserved. #include "TestNetSerializerFixture.h" #include "Iris/Core/BitTwiddling.h" #include "Iris/Serialization/IntRangeNetSerializers.h" #include namespace UE::Net::Private { template static FTestMessage& PrintIntRangeNetSerializerConfig(FTestMessage& Message, const FNetSerializerConfig& InConfig) { const SerializerConfig& Config = static_cast(InConfig); return Message << "SmallestValue: " << Config.LowerBound << " LargestValue: " << Config.UpperBound << " BitCount: " << Config.BitCount; } template class FTestIntRangeNetSerializer : public TTestNetSerializerFixture, SourceType> { typedef TTestNetSerializerFixture, SourceType> Super; public: typedef InSerializerConfig SerializerConfig; FTestIntRangeNetSerializer(const FNetSerializer& Serializer) : Super(Serializer) { SetupConfigs(); } void TestIsEqual(); void TestValidate(); void TestQuantize(); void TestSerialize(); void TestSerializeDelta(); private: void SetupConfigs(); protected: static const SourceType Values[]; static const SIZE_T ValueCount; SerializerConfig Configs[4]; static const SIZE_T ConfigCount; static constexpr SIZE_T FullRangeConfigIndex = 0; }; // FInt8RangeNetSerializer tests class FTestInt8RangeNetSerializer : public FTestIntRangeNetSerializer { public: FTestInt8RangeNetSerializer() : FTestIntRangeNetSerializer(UE_NET_GET_SERIALIZER(FInt8RangeNetSerializer)) {} }; UE_NET_TEST_FIXTURE(FTestInt8RangeNetSerializer, HasTestValues) { UE_NET_ASSERT_GT_MSG(ValueCount, SIZE_T(0), "No test values found"); } UE_NET_TEST_FIXTURE(FTestInt8RangeNetSerializer, HasTestConfigs) { UE_NET_ASSERT_GT_MSG(ConfigCount, SIZE_T(0), "No configs found"); } UE_NET_TEST_FIXTURE(FTestInt8RangeNetSerializer, TestIsEqual) { TestIsEqual(); } UE_NET_TEST_FIXTURE(FTestInt8RangeNetSerializer, TestValidate) { TestValidate(); } UE_NET_TEST_FIXTURE(FTestInt8RangeNetSerializer, TestQuantize) { TestQuantize(); } UE_NET_TEST_FIXTURE(FTestInt8RangeNetSerializer, TestSerialize) { TestSerialize(); } UE_NET_TEST_FIXTURE(FTestInt8RangeNetSerializer, TestSerializeDelta) { TestSerializeDelta(); } // FInt16RangeNetSerializer tests class FTestInt16RangeNetSerializer : public FTestIntRangeNetSerializer { public: FTestInt16RangeNetSerializer() : FTestIntRangeNetSerializer(UE_NET_GET_SERIALIZER(FInt16RangeNetSerializer)) {} }; UE_NET_TEST_FIXTURE(FTestInt16RangeNetSerializer, HasTestValues) { UE_NET_ASSERT_GT_MSG(ValueCount, SIZE_T(0), "No test values found"); } UE_NET_TEST_FIXTURE(FTestInt16RangeNetSerializer, HasTestConfigs) { UE_NET_ASSERT_GT_MSG(ConfigCount, SIZE_T(0), "No configs found"); } UE_NET_TEST_FIXTURE(FTestInt16RangeNetSerializer, TestIsEqual) { TestIsEqual(); } UE_NET_TEST_FIXTURE(FTestInt16RangeNetSerializer, TestValidate) { TestValidate(); } UE_NET_TEST_FIXTURE(FTestInt16RangeNetSerializer, TestQuantize) { TestQuantize(); } UE_NET_TEST_FIXTURE(FTestInt16RangeNetSerializer, TestSerialize) { TestSerialize(); } UE_NET_TEST_FIXTURE(FTestInt16RangeNetSerializer, TestSerializeDelta) { TestSerializeDelta(); } // FInt32RangeNetSerializer tests class FTestInt32RangeNetSerializer : public FTestIntRangeNetSerializer { public: FTestInt32RangeNetSerializer() : FTestIntRangeNetSerializer(UE_NET_GET_SERIALIZER(FInt32RangeNetSerializer)) {} }; UE_NET_TEST_FIXTURE(FTestInt32RangeNetSerializer, HasTestValues) { UE_NET_ASSERT_GT_MSG(ValueCount, SIZE_T(0), "No test values found"); } UE_NET_TEST_FIXTURE(FTestInt32RangeNetSerializer, HasTestConfigs) { UE_NET_ASSERT_GT_MSG(ConfigCount, SIZE_T(0), "No configs found"); } UE_NET_TEST_FIXTURE(FTestInt32RangeNetSerializer, TestIsEqual) { TestIsEqual(); } UE_NET_TEST_FIXTURE(FTestInt32RangeNetSerializer, TestValidate) { TestValidate(); } UE_NET_TEST_FIXTURE(FTestInt32RangeNetSerializer, TestQuantize) { TestQuantize(); } UE_NET_TEST_FIXTURE(FTestInt32RangeNetSerializer, TestSerialize) { TestSerialize(); } UE_NET_TEST_FIXTURE(FTestInt32RangeNetSerializer, TestSerializeDelta) { TestSerializeDelta(); } // FInt64RangeNetSerializer tests class FTestInt64RangeNetSerializer : public FTestIntRangeNetSerializer { public: FTestInt64RangeNetSerializer() : FTestIntRangeNetSerializer(UE_NET_GET_SERIALIZER(FInt64RangeNetSerializer)) {} }; UE_NET_TEST_FIXTURE(FTestInt64RangeNetSerializer, HasTestValues) { UE_NET_ASSERT_GT_MSG(ValueCount, SIZE_T(0), "No test values found"); } UE_NET_TEST_FIXTURE(FTestInt64RangeNetSerializer, HasTestConfigs) { UE_NET_ASSERT_GT_MSG(ConfigCount, SIZE_T(0), "No configs found"); } UE_NET_TEST_FIXTURE(FTestInt64RangeNetSerializer, TestIsEqual) { TestIsEqual(); } UE_NET_TEST_FIXTURE(FTestInt64RangeNetSerializer, TestValidate) { TestValidate(); } UE_NET_TEST_FIXTURE(FTestInt64RangeNetSerializer, TestQuantize) { TestQuantize(); } UE_NET_TEST_FIXTURE(FTestInt64RangeNetSerializer, TestSerialize) { TestSerialize(); } UE_NET_TEST_FIXTURE(FTestInt64RangeNetSerializer, TestSerializeDelta) { TestSerializeDelta(); } // template const SourceType FTestIntRangeNetSerializer::Values[] = { std::numeric_limits::min(), std::numeric_limits::max(), SourceType(0), SourceType(-1), SourceType(-16), SourceType(2048458 % std::numeric_limits::max()) }; template const SIZE_T FTestIntRangeNetSerializer::ValueCount = sizeof(Values)/sizeof(Values[0]); template const SIZE_T FTestIntRangeNetSerializer::ConfigCount = sizeof(Configs)/sizeof(Configs[0]); template void FTestIntRangeNetSerializer::SetupConfigs() { // Full range { static_assert(FullRangeConfigIndex == 0, "Incorrect NetSerializerConfig setup"); SerializerConfig& Config = Configs[0]; Config.LowerBound = std::numeric_limits::min(); Config.UpperBound = std::numeric_limits::max(); Config.BitCount = static_cast(GetBitsNeededForRange(Config.LowerBound, Config.UpperBound)); } // Zero range { SerializerConfig& Config = Configs[1]; Config.LowerBound = -64; Config.UpperBound = -64; Config.BitCount = static_cast(GetBitsNeededForRange(Config.LowerBound, Config.UpperBound)); } // Arbitrary ranges { SerializerConfig& Config = Configs[2]; Config.LowerBound = std::numeric_limits::min()/2; Config.UpperBound = std::numeric_limits::max()/4; Config.BitCount = static_cast(GetBitsNeededForRange(Config.LowerBound, Config.UpperBound)); } { SerializerConfig& Config = Configs[3]; Config.LowerBound = std::numeric_limits::max()/4; Config.UpperBound = std::numeric_limits::max()/2; Config.BitCount = static_cast(GetBitsNeededForRange(Config.LowerBound, Config.UpperBound)); } } template void FTestIntRangeNetSerializer::TestIsEqual() { SourceType CompareValues[2][sizeof(Values)/sizeof(Values[0])]; bool ExpectedResults[2][sizeof(Values)/sizeof(Values[0])]; for (SIZE_T ValueIt = 0; ValueIt < ValueCount; ++ValueIt) { CompareValues[0][ValueIt] = Values[ValueIt]; ExpectedResults[0][ValueIt] = true; } // Do two rounds of testing per config, one where we compare each value with itself and one where we compare against a value in range. for (SIZE_T ConfigIt = 0; ConfigIt != ConfigCount; ++ConfigIt) { const SerializerConfig& Config = Configs[ConfigIt]; for (SIZE_T ValueIt = 0; ValueIt < ValueCount; ++ValueIt) { CompareValues[1][ValueIt] = Config.UpperBound; ExpectedResults[1][ValueIt] = (FMath::Clamp(CompareValues[1][ValueIt], Config.LowerBound, Config.UpperBound) == (FMath::Clamp(Values[ValueIt], Config.LowerBound, Config.UpperBound))); } for (SIZE_T TestRoundIt = 0, TestRoundEndIt = 2; TestRoundIt != TestRoundEndIt; ++TestRoundIt) { constexpr bool bQuantizedCompare = false; const bool bSuccess = Super::TestIsEqual(Values, CompareValues[TestRoundIt], ExpectedResults[TestRoundIt], ValueCount, Config, bQuantizedCompare); if (!bSuccess) { return; } } } } template void FTestIntRangeNetSerializer::TestValidate() { bool ExpectedResults[sizeof(Values)/sizeof(Values[0])]; for (SIZE_T ConfigIt = 0; ConfigIt != ConfigCount; ++ConfigIt) { const SerializerConfig& Config = Configs[ConfigIt]; for (SIZE_T ValueIt = 0; ValueIt < ValueCount; ++ValueIt) { const SourceType Value = Values[ValueIt]; ExpectedResults[ValueIt] = (FMath::Clamp(Value, Config.LowerBound, Config.UpperBound) == Value); if (ConfigIt == FullRangeConfigIndex) { UE_NET_ASSERT_TRUE_MSG(ExpectedResults[ValueIt], "Clamping of value with full bit precision resulted in value being clamped. This is unexpected. Make sure no undefined behavior is used in code."); } } const bool bSuccess = Super::TestValidate(Values, ExpectedResults, ValueCount, Config); if (!bSuccess) { return; } } } template void FTestIntRangeNetSerializer::TestQuantize() { for (SIZE_T ConfigIt = 0; ConfigIt != ConfigCount; ++ConfigIt) { const SerializerConfig& Config = Configs[ConfigIt]; const bool bSuccess = Super::TestQuantize(Values, ValueCount, Config); if (!bSuccess) { return; } } } template void FTestIntRangeNetSerializer::TestSerialize() { SourceType ExpectedValues[sizeof(Values) / sizeof(Values[0])]; for (SIZE_T ValueIt = 0; ValueIt < ValueCount; ++ValueIt) { ExpectedValues[ValueIt] = Values[ValueIt]; } const auto& EqualityFunc = [](NetSerializerValuePointer Value0, NetSerializerValuePointer Value1) -> bool { return *reinterpret_cast(Value0) == *reinterpret_cast(Value1); }; for (SIZE_T ConfigIt = 0; ConfigIt != ConfigCount; ++ConfigIt) { const SerializerConfig& Config = Configs[ConfigIt]; constexpr bool bQuantizedCompare = false; TOptional> CompareFunc; if (ConfigIt == FullRangeConfigIndex) { CompareFunc = EqualityFunc; } const bool bSuccess = Super::TestSerialize(Values, ExpectedValues, ValueCount, Config, bQuantizedCompare, CompareFunc); if (!bSuccess) { return; } } } template void FTestIntRangeNetSerializer::TestSerializeDelta() { for (SIZE_T ConfigIt = 0; ConfigIt != ConfigCount; ++ConfigIt) { const SerializerConfig& Config = Configs[ConfigIt]; const bool bSuccess = Super::TestSerializeDelta(Values, ValueCount, Config); if (!bSuccess) { return; } } } }