// Copyright Epic Games, Inc. All Rights Reserved. #include "TestNetSerializerFixture.h" #include "Iris/Core/BitTwiddling.h" #include "Iris/Serialization/EnumNetSerializers.h" #include "Iris/Serialization/InternalEnumNetSerializers.h" #include "EnumTestTypes.h" #include namespace UE::Net::Private { template static FTestMessage& PrintEnumNetSerializerConfig(FTestMessage& Message, const FNetSerializerConfig& InConfig) { const SerializerConfig& Config = static_cast(InConfig); Message << "LowerBound: " << Config.LowerBound << " UpperBound: " << Config.UpperBound << " BitCount: " << Config.BitCount; if (const UEnum* Enum = Config.Enum) { Message << " Enum: " << Enum->GetName(); } return Message; } template class FTestEnumIntNetSerializer : public TTestNetSerializerFixture, SourceType> { typedef TTestNetSerializerFixture, SourceType> Super; public: FTestEnumIntNetSerializer(const FNetSerializer& Serializer) : Super(Serializer) {} void TestIsEqual(); void TestValidate(); void TestQuantize(); void TestSerialize(); void TestSerializeDelta(); protected: virtual void SetUp() override; static TArray Values; static TArray InvalidValues; static SerializerConfig Config; static const UEnum* Enum; }; #define UE_NET_IMPLEMENT_ENUMINT_NETSERIALIZER_TEST(TestClassName, NetSerializerType, ConfigType, EnumType, BackingType) \ class TestClassName : public FTestEnumIntNetSerializer \ { \ public: \ TestClassName() : FTestEnumIntNetSerializer(UE_NET_GET_SERIALIZER(NetSerializerType)) {} \ }; \ \ UE_NET_TEST_FIXTURE(TestClassName, TestIsEqual) \ { \ TestIsEqual(); \ } \ \ UE_NET_TEST_FIXTURE(TestClassName, TestValidate) \ { \ TestValidate(); \ } \ \ UE_NET_TEST_FIXTURE(TestClassName, TestQuantize) \ { \ TestQuantize(); \ } \ \ UE_NET_TEST_FIXTURE(TestClassName, TestSerialize) \ { \ TestSerialize(); \ } \ \ UE_NET_TEST_FIXTURE(TestClassName, TestSerializeDelta) \ { \ TestSerializeDelta(); \ } // EnumNetSerializer tests UE_NET_IMPLEMENT_ENUMINT_NETSERIALIZER_TEST(FTestEnumInt8NetSerializer, FEnumInt8NetSerializer, FEnumInt8NetSerializerConfig, ETestInt8Enum, int8); UE_NET_IMPLEMENT_ENUMINT_NETSERIALIZER_TEST(FTestEnumInt16NetSerializer, FEnumInt16NetSerializer, FEnumInt16NetSerializerConfig, ETestInt16Enum, int16); UE_NET_IMPLEMENT_ENUMINT_NETSERIALIZER_TEST(FTestEnumInt32NetSerializer, FEnumInt32NetSerializer, FEnumInt32NetSerializerConfig, ETestInt32Enum, int32); UE_NET_IMPLEMENT_ENUMINT_NETSERIALIZER_TEST(FTestEnumInt64NetSerializer, FEnumInt64NetSerializer, FEnumInt64NetSerializerConfig, ETestInt64Enum, int64); UE_NET_IMPLEMENT_ENUMINT_NETSERIALIZER_TEST(FTestEnumUint8NetSerializer, FEnumUint8NetSerializer, FEnumUint8NetSerializerConfig, ETestUint8Enum, uint8); UE_NET_IMPLEMENT_ENUMINT_NETSERIALIZER_TEST(FTestEnumUint16NetSerializer, FEnumUint16NetSerializer, FEnumUint16NetSerializerConfig, ETestUint16Enum, uint16); UE_NET_IMPLEMENT_ENUMINT_NETSERIALIZER_TEST(FTestEnumUint32NetSerializer, FEnumUint32NetSerializer, FEnumUint32NetSerializerConfig, ETestUint32Enum, uint32); UE_NET_IMPLEMENT_ENUMINT_NETSERIALIZER_TEST(FTestEnumUint64NetSerializer, FEnumUint64NetSerializer, FEnumUint64NetSerializerConfig, ETestUint64Enum, uint64); #undef UE_NET_IMPLEMENT_ENUMINT_NETSERIALIZER_TEST template TArray FTestEnumIntNetSerializer::Values; template TArray FTestEnumIntNetSerializer::InvalidValues; template SerializerConfig FTestEnumIntNetSerializer::Config; template const UEnum* FTestEnumIntNetSerializer::Enum; template void FTestEnumIntNetSerializer::SetUp() { static bool bIsInitialized; if (bIsInitialized) { return; } Enum = StaticEnum(); InitEnumNetSerializerConfig(Config, Enum); // Setup test values { // NumEnums actually also contain the generated _MAX enum value which might not even be a valid value by the backed type. Skip it! const int32 EnumValueCount = Enum->NumEnums() - 1; using LargeIntegerType = std::conditional_t::Value, int64, uint64>; // Valid values TArray TempValues; TempValues.Reserve(EnumValueCount); for (int32 EnumIt = 0, EnumEndIt = EnumValueCount; EnumIt != EnumEndIt; ++EnumIt) { const LargeIntegerType Value = static_cast(Enum->GetValueByIndex(EnumIt)); TempValues.Add(static_cast(Value)); } Values = MoveTemp(TempValues); // Invalid values TArray TempInvalidValues; TempInvalidValues.Reserve(3); if (Config.LowerBound > std::numeric_limits::min()) { TempInvalidValues.Add(Config.LowerBound - SourceType(1)); } if (Config.UpperBound < std::numeric_limits::max()) { TempInvalidValues.Add(Config.UpperBound + SourceType(1)); } // Try adding an invalid value between the smallest and largest values found for (SourceType Value = Config.LowerBound, UpperBound = Config.UpperBound; Value != UpperBound; ++Value) { if (!Enum->IsValidEnumValue(Value)) { TempInvalidValues.Add(Value); break; } } InvalidValues = MoveTemp(TempInvalidValues); } bIsInitialized = true; } template void FTestEnumIntNetSerializer::TestIsEqual() { TArray CompareValues[2]; TArray ExpectedResults[2]; CompareValues[0] = Values; ExpectedResults[0].Reserve(Values.Num()); for (int32 ValueIt = 0, ValueEndIt = Values.Num(); ValueIt != ValueEndIt; ++ValueIt) { ExpectedResults[0].Add(true); } CompareValues[1].Reserve(Values.Num()); ExpectedResults[1].Reserve(Values.Num()); for (int32 ValueIt = 0, ValueEndIt = Values.Num(); ValueIt != ValueEndIt; ++ValueIt) { CompareValues[1].Add(Values[(ValueIt + 1) % ValueEndIt]); ExpectedResults[1].Add(Values[ValueIt] == Values[(ValueIt + 1) % ValueEndIt]); } // 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 TestRoundIt = 0, TestRoundEndIt = 2; TestRoundIt != TestRoundEndIt; ++TestRoundIt) { // Do both quantized and regular compares for (SIZE_T CompareIt = 0; CompareIt != 2; ++CompareIt) { bool bQuantizedCompare = CompareIt == 0; const bool bSuccess = Super::TestIsEqual(Values.GetData(), CompareValues[TestRoundIt].GetData(), ExpectedResults[TestRoundIt].GetData(), Values.Num(), Config, bQuantizedCompare); if (!bSuccess) { return; } } } } template void FTestEnumIntNetSerializer::TestValidate() { // Check valid values { TArray ExpectedResults; ExpectedResults.SetNumUninitialized(Values.Num()); for (int32 ValueIt = 0, ValueEndIt = Values.Num(); ValueIt != ValueEndIt; ++ValueIt) { ExpectedResults[ValueIt] = true; } const bool bSuccess = Super::TestValidate(Values.GetData(), ExpectedResults.GetData(), Values.Num(), Config); if (!bSuccess) { return; } } // Check invalid values { UE_NET_EXPECT_GT_MSG(InvalidValues.Num(), 0, "Unable to test EnumIntSerializer Validate with invalid values."); TArray ExpectedResults; ExpectedResults.SetNumZeroed(InvalidValues.Num()); const bool bSuccess = Super::TestValidate(InvalidValues.GetData(), ExpectedResults.GetData(), InvalidValues.Num(), Config); if (!bSuccess) { return; } } } template void FTestEnumIntNetSerializer::TestQuantize() { Super::TestQuantize(Values.GetData(), Values.Num(), Config); } template void FTestEnumIntNetSerializer::TestSerialize() { constexpr bool bQuantizedCompare = false; Super::TestSerialize(Values.GetData(), Values.GetData(), Values.Num(), Config, bQuantizedCompare); } template void FTestEnumIntNetSerializer::TestSerializeDelta() { Super::TestSerializeDelta(Values.GetData(), Values.Num(), Config); } }