Files
UnrealEngine/Engine/Plugins/Runtime/ReplicationSystemTestPlugin/Source/Private/Tests/Serialization/TestStringNetSerializer.cpp
2025-05-18 13:04:45 +08:00

336 lines
14 KiB
C++

// 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<PrintStringNetSerializerConfig, FString>
{
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<PrintStringNetSerializerConfig, FString> 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<FString> WellFormedTestStrings;
static TArray<FString> 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<FString> FTestStringNetSerializer::WellFormedTestStrings;
TArray<FString> 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<PrintStringNetSerializerConfig, FString>::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<bool> 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<FString> CompareValues[2];
TArray<bool> 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<bool> 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);
}
}
}
}