// Copyright Epic Games, Inc. All Rights Reserved. #include "TestNetSerializerFixture.h" #include "Iris/Serialization/StringNetSerializers.h" #include "Containers/StringConv.h" namespace UE::Net::Private { static FTestMessage& PrintStringNetSerializerConfig(FTestMessage& Message, const FNetSerializerConfig& InConfig) { return Message; } class FTestStringNetSerializer : public TTestNetSerializerFixture { public: FTestStringNetSerializer() : Super(UE_NET_GET_SERIALIZER(FStringNetSerializer)) {} void TestValidate(); void TestQuantize(); void TestIsEqual(); void TestSerialize(); void TestCloneDynamicState(); void TestIllFormedStringIsEqual(); void TestIllFormedStringSerialize(); protected: typedef TTestNetSerializerFixture Super; virtual void SetUp() override; // Set of valid strings static const char* UTF8Strings[]; static const SIZE_T UTF8StringCount; // Valid and invalid strings. These are the ones to be tested. static TArray WellFormedTestStrings; static TArray IllFormedTestStrings; // Serializer static FStringNetSerializerConfig SerializerConfig; }; // These are const char* FTestStringNetSerializer::UTF8Strings[] = { // Empty string "", // Pure ASCII string "Just a regular ASCII string", // Smiling face with open mouth and tightly-closed eyes, four of circles, euro, copyright "\xf0\x9f\x98\x86\xf0\x9f\x80\x9c\xe2\x82\xac\xc2\xa9", // A long string consisting of 5000 characters. This is the last valid string to test. "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789", }; const SIZE_T FTestStringNetSerializer::UTF8StringCount = sizeof(UTF8Strings)/sizeof(UTF8Strings[0]); TArray FTestStringNetSerializer::WellFormedTestStrings; TArray FTestStringNetSerializer::IllFormedTestStrings; FStringNetSerializerConfig FTestStringNetSerializer::SerializerConfig; UE_NET_TEST_FIXTURE(FTestStringNetSerializer, TestValidate) { TestValidate(); } UE_NET_TEST_FIXTURE(FTestStringNetSerializer, TestQuantize) { TestQuantize(); } UE_NET_TEST_FIXTURE(FTestStringNetSerializer, TestIsEqual) { TestIsEqual(); } UE_NET_TEST_FIXTURE(FTestStringNetSerializer, TestSerialize) { TestSerialize(); } UE_NET_TEST_FIXTURE(FTestStringNetSerializer, TestCloneDynamicState) { TestCloneDynamicState(); } UE_NET_TEST_FIXTURE(FTestStringNetSerializer, TestIllFormedStringIsEqual) { TestIllFormedStringIsEqual(); } UE_NET_TEST_FIXTURE(FTestStringNetSerializer, TestIllFormedStringSerialize) { TestIllFormedStringSerialize(); } void FTestStringNetSerializer::SetUp() { TTestNetSerializerFixture::SetUp(); static bool bAreStringsInitialized = false; if (!bAreStringsInitialized) { bAreStringsInitialized = true; WellFormedTestStrings.Reserve(UTF8StringCount); for (const char* UTF8String : MakeArrayView(UTF8Strings, UTF8StringCount)) { WellFormedTestStrings.Add(FString(FUTF8ToTCHAR(UTF8String).Get())); } { IllFormedTestStrings.Reserve(3); IllFormedTestStrings.Add(FString(TEXT("Single low surrogate: '\xD800'."))); IllFormedTestStrings.Add(FString(TEXT("Single high surrogate: '\xDC00'."))); IllFormedTestStrings.Add(FString(TEXT("Invalid character: '\xFFFF'."))); } } } void FTestStringNetSerializer::TestValidate() { // Check valid strings { TArray ExpectedResults; ExpectedResults.Init(true, WellFormedTestStrings.Num()); const bool bSuccess = Super::TestValidate(WellFormedTestStrings.GetData(), ExpectedResults.GetData(), WellFormedTestStrings.Num(), SerializerConfig); if (!bSuccess) { return; } } } void FTestStringNetSerializer::TestQuantize() { const bool bSuccess = Super::TestQuantize(WellFormedTestStrings.GetData(), WellFormedTestStrings.Num(), SerializerConfig); if (!bSuccess) { return; } } void FTestStringNetSerializer::TestIsEqual() { TArray CompareValues[2]; TArray ExpectedResults[2]; CompareValues[0] = WellFormedTestStrings; ExpectedResults[0].Init(true, WellFormedTestStrings.Num()); CompareValues[1].Reserve(WellFormedTestStrings.Num()); ExpectedResults[1].Reserve(WellFormedTestStrings.Num()); for (int32 ValueIt = 0, ValueEndIt = WellFormedTestStrings.Num(); ValueIt != ValueEndIt; ++ValueIt) { CompareValues[1].Add(WellFormedTestStrings[(ValueIt + 1) % ValueEndIt]); ExpectedResults[1].Add(WellFormedTestStrings[ValueIt].Equals(WellFormedTestStrings[(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, 1}) { // Do both quantized and regular compares for (SIZE_T CompareIt : {0, 1}) { bool bQuantizedCompare = CompareIt == 0; const bool bSuccess = Super::TestIsEqual(WellFormedTestStrings.GetData(), CompareValues[TestRoundIt].GetData(), ExpectedResults[TestRoundIt].GetData(), WellFormedTestStrings.Num(), SerializerConfig, bQuantizedCompare); if (!bSuccess) { return; } } } } void FTestStringNetSerializer::TestSerialize() { constexpr bool bQuantizedCompare = false; const bool bSuccess = Super::TestSerialize(WellFormedTestStrings.GetData(), WellFormedTestStrings.GetData(), WellFormedTestStrings.Num(), SerializerConfig, bQuantizedCompare); if (!bSuccess) { return; } } void FTestStringNetSerializer::TestCloneDynamicState() { const bool bSuccess = Super::TestCloneDynamicState(WellFormedTestStrings.GetData(), WellFormedTestStrings.Num(), SerializerConfig); if (!bSuccess) { return; } } void FTestStringNetSerializer::TestIllFormedStringIsEqual() { // Do both quantized and regular compares. We expect the strings to be equal to themselves. TArray ExpectedResults; ExpectedResults.Init(true, IllFormedTestStrings.Num()); for (SIZE_T CompareIt : {0, 1}) { const bool bQuantizedCompare = CompareIt == 0; const bool bSuccess = Super::TestIsEqual(IllFormedTestStrings.GetData(), IllFormedTestStrings.GetData(), ExpectedResults.GetData(), IllFormedTestStrings.Num(), SerializerConfig, bQuantizedCompare); if (!bSuccess) { return; } } } void FTestStringNetSerializer::TestIllFormedStringSerialize() { // Just make sure nothing blows up when ill-formed strings are replicated. // N.B. This test will leak some memory if any of the tests fail. for (const FString& SourceString : IllFormedTestStrings) { alignas(16) uint8 QuantizedStateBuffer[2][128] = {}; FString TargetString; // Quantize { FNetQuantizeArgs QuantizeArgs; QuantizeArgs.Version = SerializerVersionOverride; QuantizeArgs.NetSerializerConfig = &SerializerConfig; QuantizeArgs.Source = NetSerializerValuePointer(&SourceString); QuantizeArgs.Target = NetSerializerValuePointer(QuantizedStateBuffer[0]); Serializer.Quantize(Context, QuantizeArgs); UE_NET_ASSERT_FALSE_MSG(Context.HasError(), "Quantize failed."); } // Serialize { Writer.InitBytes(BitStreamBuffer, sizeof(BitStreamBuffer)); FNetSerializeArgs SerializeArgs; SerializeArgs.Version = SerializerVersionOverride; SerializeArgs.NetSerializerConfig = &SerializerConfig; SerializeArgs.Source = NetSerializerValuePointer(QuantizedStateBuffer[0]); Serializer.Serialize(Context, SerializeArgs); Writer.CommitWrites(); UE_NET_ASSERT_FALSE_MSG(Context.HasError(), "Serialize() reported an error."); UE_NET_ASSERT_FALSE_MSG(Writer.IsOverflown(), "FNetBitStreamWriter overflowed."); } // Deserialize { Reader.InitBits(BitStreamBuffer, Writer.GetPosBits()); FNetDeserializeArgs DeserializeArgs; DeserializeArgs.Version = SerializerVersionOverride; DeserializeArgs.NetSerializerConfig = &SerializerConfig; DeserializeArgs.Target = NetSerializerValuePointer(QuantizedStateBuffer[1]); Serializer.Deserialize(Context, DeserializeArgs); Writer.CommitWrites(); UE_NET_ASSERT_FALSE_MSG(Context.HasError(), "Deserialize() reported an error."); UE_NET_ASSERT_FALSE_MSG(Reader.IsOverflown(), "FNetBitStreamRead overflowed."); } // Dequantize { FNetDequantizeArgs DequantizeArgs; DequantizeArgs.Version = SerializerVersionOverride; DequantizeArgs.NetSerializerConfig = &SerializerConfig; DequantizeArgs.Source = NetSerializerValuePointer(QuantizedStateBuffer[1]); DequantizeArgs.Target = NetSerializerValuePointer(&TargetString); Serializer.Dequantize(Context, DequantizeArgs); // Depending on the size of wchar_t an error may be generated or not on certain ill-formed strings. //UE_NET_ASSERT_FALSE_MSG(Context.HasError(), "Dequantize() reported an error for string '" << SourceString << '\''); } // Free memory { FNetFreeDynamicStateArgs FreeArgs; FreeArgs.Version = SerializerVersionOverride; FreeArgs.NetSerializerConfig = &SerializerConfig; FreeArgs.Source = NetSerializerValuePointer(QuantizedStateBuffer[0]); Serializer.FreeDynamicState(Context, FreeArgs); FreeArgs.Source = NetSerializerValuePointer(QuantizedStateBuffer[1]); Serializer.FreeDynamicState(Context, FreeArgs); } } } }