#include "Testing/StringTesting.h" #include "String/Char.h" #include "Memory/Memory.h" #include "String/String.h" #include "String/StringView.h" #include "Miscellaneous/AssertionMacros.h" #include NAMESPACE_REDCRAFT_BEGIN NAMESPACE_MODULE_BEGIN(Redcraft) NAMESPACE_MODULE_BEGIN(Utility) NAMESPACE_BEGIN(Testing) void TestString() { TestChar(); TestStringView(); TestTemplateString(); TestStringConversion(); } void TestChar() { { always_check(!CCharType); always_check(CCharType); always_check(CCharType); always_check(CCharType); always_check(CCharType); always_check(CCharType); always_check(CCharType); } auto Test = [](TInPlaceType) { always_check(TChar::IsASCII(LITERAL(T, '0'))); always_check(TChar::IsASCII(LITERAL(T, 'A'))); always_check(TChar::IsASCII(LITERAL(T, 'a'))); always_check(TChar::IsASCII(LITERAL(T, 'A'))); always_check(TChar::IsASCII(LITERAL(T, '0'))); always_check(TChar::IsASCII(LITERAL(T, '\n'))); always_check(TChar::IsASCII(LITERAL(T, '!'))); always_check(TChar::IsASCII(LITERAL(T, '\t'))); always_check(TChar::IsASCII(LITERAL(T, ' '))); always_check(TChar::IsASCII(LITERAL(T, '#'))); always_check(TChar::IsASCII(LITERAL(T, '['))); always_check(TChar::IsAlnum(LITERAL(T, '0'))); always_check(TChar::IsAlpha(LITERAL(T, 'A'))); always_check(TChar::IsLower(LITERAL(T, 'a'))); always_check(TChar::IsUpper(LITERAL(T, 'A'))); always_check(TChar::IsDigit(LITERAL(T, '0'))); always_check(TChar::IsCntrl(LITERAL(T, '\n'))); always_check(TChar::IsGraph(LITERAL(T, '!'))); always_check(TChar::IsSpace(LITERAL(T, '\t'))); always_check(TChar::IsBlank(LITERAL(T, ' '))); always_check(TChar::IsPrint(LITERAL(T, '#'))); always_check(TChar::IsPunct(LITERAL(T, '['))); always_check(!TChar::IsAlnum(LITERAL(T, '$'))); always_check(!TChar::IsAlpha(LITERAL(T, '0'))); always_check(!TChar::IsLower(LITERAL(T, 'A'))); always_check(!TChar::IsUpper(LITERAL(T, 'a'))); always_check(!TChar::IsDigit(LITERAL(T, 'I'))); always_check(!TChar::IsCntrl(LITERAL(T, '_'))); always_check(!TChar::IsGraph(LITERAL(T, ' '))); always_check(!TChar::IsSpace(LITERAL(T, '='))); always_check(!TChar::IsBlank(LITERAL(T, '+'))); always_check(!TChar::IsPrint(LITERAL(T, '\n'))); always_check(!TChar::IsPunct(LITERAL(T, 'H'))); always_check( TChar::IsDigit(LITERAL(T, 'F'), 16)); always_check(!TChar::IsDigit(LITERAL(T, 'G'), 16)); always_check(TChar::ToLower(LITERAL(T, 'i')) == LITERAL(T, 'i')); always_check(TChar::ToUpper(LITERAL(T, 'l')) == LITERAL(T, 'L')); always_check(0x0 == TChar::ToDigit(LITERAL(T, '0'), 16)); always_check(0xF == TChar::ToDigit(LITERAL(T, 'f'), 16)); always_check(0xF == TChar::ToDigit(LITERAL(T, 'F'), 16)); always_check(LITERAL(T, '0') == TChar::FromDigit(0x0, 16)); always_check(LITERAL(T, 'f') != TChar::FromDigit(0xF, 16)); always_check(LITERAL(T, 'F') == TChar::FromDigit(0xF, 16)); }; Test(InPlaceType); Test(InPlaceType); Test(InPlaceType); Test(InPlaceType); Test(InPlaceType); Test(InPlaceType); } void TestStringView() { auto Test = [](TInPlaceType) { { TStringView Empty; always_check(Empty == LITERAL(T, "")); TStringView ViewI = LITERAL(T, "#Hello, World! Goodbye, World!#"); ViewI.RemovePrefix(1); ViewI.RemoveSuffix(1); T Buffer[64]; Memory::Memzero(Buffer); ViewI.Copy(Buffer); TStringView ViewII = Buffer; always_check(ViewI == LITERAL(T, "Hello, World! Goodbye, World!")); always_check(ViewII == LITERAL(T, "Hello, World! Goodbye, World!")); TStringView ViewA(ViewI.Begin(), 13); TStringView ViewB(ViewI.Begin(), ViewI.End()); TStringView ViewC(&Buffer[0], 13); TStringView ViewD(&Buffer[0]); always_check(ViewA == LITERAL(T, "Hello, World!")); always_check(ViewB == LITERAL(T, "Hello, World! Goodbye, World!")); always_check(ViewC == LITERAL(T, "Hello, World!")); always_check(ViewD == LITERAL(T, "Hello, World! Goodbye, World!")); } { TStringView View = LITERAL(T, "Hello, World! Goodbye, World!"); always_check( View.StartsWith(LITERAL(T, "Hello, World!"))); always_check(!View.StartsWith(LITERAL(T, "Goodbye, World!"))); always_check( View.StartsWith(LITERAL(T, 'H'))); always_check(!View.StartsWith(LITERAL(T, 'G'))); always_check(!View.EndsWith(LITERAL(T, "Hello, World!"))); always_check( View.EndsWith(LITERAL(T, "Goodbye, World!"))); always_check( View.EndsWith(LITERAL(T, '!'))); always_check(!View.EndsWith(LITERAL(T, '?'))); always_check( View.Contains(LITERAL(T, "Hello, World!"))); always_check( View.Contains(LITERAL(T, "Goodbye, World!"))); always_check( View.Contains(LITERAL(T, '!'))); always_check(!View.Contains(LITERAL(T, '?'))); } { TStringView View = LITERAL(T, "Hello, World! Goodbye, World!"); always_check(View.Find(LITERAL(T, "")) == 0); always_check(View.Find(LITERAL(T, "World")) == 7); always_check(View.Find(LITERAL(T, 'l')) == 2); always_check(View.RFind(LITERAL(T, "")) == 29); always_check(View.RFind(LITERAL(T, "World")) == 23); always_check(View.RFind(LITERAL(T, 'l')) == 26); always_check(View.Find(LITERAL(T, ""), 13) == 13); always_check(View.Find(LITERAL(T, "World"), 13) == 23); always_check(View.Find(LITERAL(T, 'l'), 13) == 26); always_check(View.RFind(LITERAL(T, ""), 13) == 13); always_check(View.RFind(LITERAL(T, "World"), 13) == 7); always_check(View.RFind(LITERAL(T, 'l'), 13) == 10); always_check(View.FindFirstOf(LITERAL(T, "eor")) == 1); always_check(View.FindFirstOf(LITERAL(T, 'l')) == 2); always_check(View.FindLastOf(LITERAL(T, "eor")) == 25); always_check(View.FindLastOf(LITERAL(T, 'l')) == 26); always_check(View.FindFirstNotOf(LITERAL(T, "Hello! Goodbye!")) == 5); always_check(View.FindFirstNotOf(LITERAL(T, '!')) == 0); always_check(View.FindLastNotOf(LITERAL(T, "Hello! Goodbye!")) == 25); always_check(View.FindLastNotOf(LITERAL(T, '!')) == 27); } { always_check( LITERAL_VIEW(T, "012345678900").IsASCII()); always_check(!LITERAL_VIEW(T, "\u4E38\u8FA3").IsASCII()); always_check( LITERAL_VIEW(T, "012345678900").IsNumeric()); always_check(!LITERAL_VIEW(T, "\u4E38\u8FA3").IsNumeric()); always_check(!LITERAL_VIEW(T, "0123456789AB").IsNumeric()); always_check( LITERAL_VIEW(T, "0123456789AB").IsNumeric(16)); } }; Test(InPlaceType); Test(InPlaceType); Test(InPlaceType); Test(InPlaceType); Test(InPlaceType); Test(InPlaceType); } void TestTemplateString() { auto Test = [](TInPlaceType) { { TString Empty; always_check(Empty.IsEmpty()); always_check(TStringView(*Empty) == LITERAL(T, "")); TString StrA(32, LITERAL(T, 'A')); TString StrB(LITERAL(T, "ABCDEFG"), 3); TString StrC(LITERAL(T, "ABCDEFG")); TString StrD(TStringView(LITERAL(T, "ABCDEFG"))); TString StrE({ LITERAL(T, 'A'), LITERAL(T, 'B'), LITERAL(T, 'C') }); always_check(TStringView(*StrA) == LITERAL(T, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")); always_check(TStringView(*StrB) == LITERAL(T, "ABC")); always_check(TStringView(*StrC) == LITERAL(T, "ABCDEFG")); always_check(TStringView(*StrD) == LITERAL(T, "ABCDEFG")); always_check(TStringView(*StrE) == LITERAL(T, "ABC")); TString StrI(StrC); TString StrII(MoveTemp(StrC)); TString StrIII = Empty; TString StrIV = Empty; StrIII = StrD; StrIV = MoveTemp(StrD); always_check(TStringView(StrI ) == LITERAL(T, "ABCDEFG")); always_check(TStringView(StrII ) == LITERAL(T, "ABCDEFG")); always_check(TStringView(StrIII) == LITERAL(T, "ABCDEFG")); always_check(TStringView(StrIV ) == LITERAL(T, "ABCDEFG")); always_check(StrC == LITERAL(T, "")); always_check(StrD == LITERAL(T, "")); StrA.Reset(); always_check(StrA.IsEmpty()); always_check(StrA == LITERAL(T, "")); } { TString Str = LITERAL(T, "A"); always_check(!Str.IsEmpty()); always_check(Str.Num() == 1); always_check(Str == TString(LITERAL(T, "A"))); always_check(Str == LITERAL(T, 'A') ); always_check(Str == LITERAL(T, 'A') ); always_check(TString(LITERAL(T, "A")) == Str); always_check( LITERAL(T, 'A') == Str); always_check( LITERAL(T, "A") == Str); always_check(Str != TString(LITERAL(T, "B"))); always_check(Str != LITERAL(T, 'B') ); always_check(Str != LITERAL(T, "B") ); always_check(TString(LITERAL(T, "B")) != Str); always_check( LITERAL(T, 'B') != Str); always_check( LITERAL(T, "B") != Str); always_check(Str < TString(LITERAL(T, "B"))); always_check(Str < LITERAL(T, 'B') ); always_check(Str < LITERAL(T, "B") ); always_check(TString(LITERAL(T, "B")) > Str); always_check( LITERAL(T, 'B') > Str); always_check( LITERAL(T, "B") > Str); } { TString Str = LITERAL(T, "##"); Str.Insert(1, LITERAL(T, 'A')); always_check(Str == LITERAL(T, "#A#")); Str.Insert(2, LITERAL(T, "BCD")); always_check(Str == LITERAL(T, "#ABCD#")); Str.Insert(3, 3, LITERAL(T, '*')); always_check(Str == LITERAL(T, "#AB***CD#")); Str.Erase(4); always_check(Str == LITERAL(T, "#AB**CD#")); } { TString Str = LITERAL(T, "A"); Str.PushBack(LITERAL(T, 'B')); always_check(Str == LITERAL(T, "AB")); Str.PopBack(); always_check(Str == LITERAL(T, "A")); Str.Append(2, LITERAL(T, 'B')); always_check(Str == LITERAL(T, "ABB")); Str.Append(LITERAL(T, "CD")); always_check(Str == LITERAL(T, "ABBCD")); Str.Append({ LITERAL(T, 'E'), LITERAL(T, 'F') }); always_check(Str == LITERAL(T, "ABBCDEF")); Str = LITERAL(T, "A"); Str += LITERAL(T, 'B'); always_check(Str == LITERAL(T, "AB")); Str += LITERAL(T, "CD"); always_check(Str == LITERAL(T, "ABCD")); Str += { LITERAL(T, 'E'), LITERAL(T, 'F') }; always_check(Str == LITERAL(T, "ABCDEF")); } { TString StrA = LITERAL(T, "A"); TString StrB = LITERAL(T, "B"); always_check(StrA + StrB == LITERAL(T, "AB")); always_check(StrA + LITERAL(T, 'B') == LITERAL(T, "AB")); always_check(StrA + LITERAL(T, "BCD") == LITERAL(T, "ABCD")); always_check(LITERAL(T, 'B') + StrB == LITERAL(T, "BB")); always_check(LITERAL(T, "BCD") + StrB == LITERAL(T, "BCDB")); StrA = LITERAL(T, "A"); StrB = LITERAL(T, "B"); always_check(MoveTemp(StrA) + MoveTemp(StrB) == LITERAL(T, "AB")); StrA = LITERAL(T, "A"); StrB = LITERAL(T, "B"); always_check(MoveTemp(StrA) + LITERAL(T, 'B') == LITERAL(T, "AB")); StrA = LITERAL(T, "A"); StrB = LITERAL(T, "B"); always_check(MoveTemp(StrA) + LITERAL(T, "BCD") == LITERAL(T, "ABCD")); StrA = LITERAL(T, "A"); StrB = LITERAL(T, "B"); always_check(LITERAL(T, 'B') + MoveTemp(StrB) == LITERAL(T, "BB")); StrA = LITERAL(T, "A"); StrB = LITERAL(T, "B"); always_check(LITERAL(T, "BCD") + MoveTemp(StrB) == LITERAL(T, "BCDB")); } { TString Str = LITERAL(T, "Hello, World! Goodbye, World!"); always_check( Str.StartsWith(LITERAL(T, "Hello, World!"))); always_check(!Str.StartsWith(LITERAL(T, "Goodbye, World!"))); always_check( Str.StartsWith(LITERAL(T, 'H'))); always_check(!Str.StartsWith(LITERAL(T, 'G'))); always_check(!Str.EndsWith(LITERAL(T, "Hello, World!"))); always_check( Str.EndsWith(LITERAL(T, "Goodbye, World!"))); always_check( Str.EndsWith(LITERAL(T, '!'))); always_check(!Str.EndsWith(LITERAL(T, '?'))); always_check( Str.Contains(LITERAL(T, "Hello, World!"))); always_check( Str.Contains(LITERAL(T, "Goodbye, World!"))); always_check( Str.Contains(LITERAL(T, '!'))); always_check(!Str.Contains(LITERAL(T, '?'))); } { TString Str = LITERAL(T, "#AB**CD#"); always_check(Str.Replace(3, 2, 3, LITERAL(T, '^')) == LITERAL(T, "#AB^^^CD#")); always_check(Str.Replace(3, 3, LITERAL(T, "123")) == LITERAL(T, "#AB123CD#")); always_check(Str.Substr(3, 3) == LITERAL(T, "123")); always_check(Str.Substr(3) == LITERAL(T, "123CD#")); } { TString Str = LITERAL(T, "Hello, World! Goodbye, World!"); always_check(Str.Find(LITERAL(T, "")) == 0); always_check(Str.Find(LITERAL(T, "World")) == 7); always_check(Str.Find(LITERAL(T, 'l')) == 2); always_check(Str.RFind(LITERAL(T, "")) == 29); always_check(Str.RFind(LITERAL(T, "World")) == 23); always_check(Str.RFind(LITERAL(T, 'l')) == 26); always_check(Str.Find(LITERAL(T, ""), 13) == 13); always_check(Str.Find(LITERAL(T, "World"), 13) == 23); always_check(Str.Find(LITERAL(T, 'l'), 13) == 26); always_check(Str.RFind(LITERAL(T, ""), 13) == 13); always_check(Str.RFind(LITERAL(T, "World"), 13) == 7); always_check(Str.RFind(LITERAL(T, 'l'), 13) == 10); always_check(Str.FindFirstOf(LITERAL(T, "eor")) == 1); always_check(Str.FindFirstOf(LITERAL(T, 'l')) == 2); always_check(Str.FindLastOf(LITERAL(T, "eor")) == 25); always_check(Str.FindLastOf(LITERAL(T, 'l')) == 26); always_check(Str.FindFirstNotOf(LITERAL(T, "Hello! Goodbye!")) == 5); always_check(Str.FindFirstNotOf(LITERAL(T, '!')) == 0); always_check(Str.FindLastNotOf(LITERAL(T, "Hello! Goodbye!")) == 25); always_check(Str.FindLastNotOf(LITERAL(T, '!')) == 27); } { always_check(TString(LITERAL(T, "\u4E38\u8FA3")).ToString() == TEXT("\u4E38\u8FA3")); always_check(TString(LITERAL(T, "\u4E38\u8FA3")).ToWString() == WTEXT("\u4E38\u8FA3")); always_check(TString(LITERAL(T, "\u4E38\u8FA3")).ToU8String() == U8TEXT("\u4E38\u8FA3")); always_check(TString(LITERAL(T, "\u4E38\u8FA3")).ToU16String() == U16TEXT("\u4E38\u8FA3")); always_check(TString(LITERAL(T, "\u4E38\u8FA3")).ToU32String() == U32TEXT("\u4E38\u8FA3")); always_check(TString(LITERAL(T, "\u4E38\u8FA3")).ToUnicodeString() == UNICODETEXT("\u4E38\u8FA3")); } }; Test(InPlaceType); Test(InPlaceType); Test(InPlaceType); Test(InPlaceType); Test(InPlaceType); Test(InPlaceType); } void TestStringConversion() { auto Test = [](TInPlaceType) { always_check(TString::Format(LITERAL(T, "#{}#"), true ) == LITERAL(T, "#True#" )); always_check(TString::Format(LITERAL(T, "#{}#"), false) == LITERAL(T, "#False#")); always_check(TString::Format(LITERAL(T, "#{}#"), +0) == LITERAL(T, "#0#")); always_check(TString::Format(LITERAL(T, "#{}#"), 0) == LITERAL(T, "#0#")); always_check(TString::Format(LITERAL(T, "#{}#"), -0) == LITERAL(T, "#0#")); always_check(TString::Format(LITERAL(T, "#{}#"), 42) == LITERAL(T, "#42#")); always_check(TString::Format(LITERAL(T, "#{}#"), +0.0) == LITERAL(T, "#0.000000#")); always_check(TString::Format(LITERAL(T, "#{}#"), 0.0) == LITERAL(T, "#0.000000#")); always_check(TString::Format(LITERAL(T, "#{}#"), -0.0) == LITERAL(T, "#-0.000000#")); always_check(TString::Format(LITERAL(T, "#{}#"), 3.140000) == LITERAL(T, "#3.140000#")); always_check(TString::Format(LITERAL(T, "#{}#"), +NAMESPACE_STD::numeric_limits::infinity()) == LITERAL(T, "#Infinity#")); always_check(TString::Format(LITERAL(T, "#{}#"), -NAMESPACE_STD::numeric_limits::infinity()) == LITERAL(T, "#-Infinity#")); always_check(TString::Format(LITERAL(T, "#{}#"), +NAMESPACE_STD::numeric_limits::quiet_NaN()) == LITERAL(T, "#NaN#")); always_check(TString::Format(LITERAL(T, "#{}#"), -NAMESPACE_STD::numeric_limits::quiet_NaN()) == LITERAL(T, "#-NaN#")); auto CheckParseArithmetic = [](TStringView View, U Result) { U Object; always_check(View.Parse(LITERAL(T, "{0:}"), Object) == 1); if constexpr (CFloatingPoint) { always_check(NAMESPACE_STD::isinf(Result) == NAMESPACE_STD::isinf(Object)); always_check(NAMESPACE_STD::isnan(Result) == NAMESPACE_STD::isnan(Object)); always_check(NAMESPACE_STD::signbit(Result) == NAMESPACE_STD::signbit(Object)); if (NAMESPACE_STD::isinf(Result) || NAMESPACE_STD::isnan(Result)) return; constexpr auto Epsilon = 1e-3; always_check(NAMESPACE_STD::abs(Object - Result) < Epsilon); } else always_check(Object == Result); }; CheckParseArithmetic(LITERAL(T, "true" ), true ); CheckParseArithmetic(LITERAL(T, "false"), false); auto CheckParseInt = [&](TInPlaceType) { CheckParseArithmetic(LITERAL(T, "+0"), static_cast(+0.0)); CheckParseArithmetic(LITERAL(T, " 0"), static_cast( 0.0)); CheckParseArithmetic(LITERAL(T, "-0"), static_cast(-0.0)); CheckParseArithmetic(LITERAL(T, "+42"), static_cast( +42)); CheckParseArithmetic(LITERAL(T, "+052"), static_cast( +052)); CheckParseArithmetic(LITERAL(T, "+0x2A"), static_cast( +0x2A)); CheckParseArithmetic(LITERAL(T, "+0b101010"), static_cast(+0b101010)); CheckParseArithmetic(LITERAL(T, "42"), static_cast( 42)); CheckParseArithmetic(LITERAL(T, "052"), static_cast( 052)); CheckParseArithmetic(LITERAL(T, "0x2A"), static_cast( 0x2A)); CheckParseArithmetic(LITERAL(T, "0b101010"), static_cast(0b101010)); CheckParseArithmetic(LITERAL(T, "-42"), static_cast( -42)); CheckParseArithmetic(LITERAL(T, "-052"), static_cast( -052)); CheckParseArithmetic(LITERAL(T, "-0x2A"), static_cast( -0x2A)); CheckParseArithmetic(LITERAL(T, "-0b101010"), static_cast(-0b101010)); }; CheckParseInt(InPlaceType); CheckParseInt(InPlaceType); CheckParseInt(InPlaceType); CheckParseInt(InPlaceType); CheckParseInt(InPlaceType); CheckParseInt(InPlaceType); CheckParseInt(InPlaceType); CheckParseInt(InPlaceType); CheckParseInt(InPlaceType); auto CheckParseFloat = [&](TInPlaceType) { CheckParseInt(InPlaceType); CheckParseArithmetic(LITERAL(T, "+3.14"), static_cast( +3.14)); CheckParseArithmetic(LITERAL(T, "+3.14e2"), static_cast( +3.14e2)); CheckParseArithmetic(LITERAL(T, "+3.14e-2"), static_cast( +3.14e-2)); CheckParseArithmetic(LITERAL(T, "+0x1.91eb86p1"), static_cast(+0x1.91eb86p1)); CheckParseArithmetic(LITERAL(T, "3.14"), static_cast( 3.14)); CheckParseArithmetic(LITERAL(T, "3.14e2"), static_cast( 3.14e2)); CheckParseArithmetic(LITERAL(T, "3.14e-2"), static_cast( 3.14e-2)); CheckParseArithmetic(LITERAL(T, "0x1.91eb86p1"), static_cast(0x1.91eb86p1)); CheckParseArithmetic(LITERAL(T, "-3.14"), static_cast( -3.14)); CheckParseArithmetic(LITERAL(T, "-3.14e2"), static_cast( -3.14e2)); CheckParseArithmetic(LITERAL(T, "-3.14e-2"), static_cast( -3.14e-2)); CheckParseArithmetic(LITERAL(T, "-0x1.91eb86p1"), static_cast(-0x1.91eb86p1)); CheckParseArithmetic(LITERAL(T, "+Infinity"), +NAMESPACE_STD::numeric_limits::infinity()); CheckParseArithmetic(LITERAL(T, " Infinity"), +NAMESPACE_STD::numeric_limits::infinity()); CheckParseArithmetic(LITERAL(T, "-Infinity"), -NAMESPACE_STD::numeric_limits::infinity()); CheckParseArithmetic(LITERAL(T, "+NaN"), +NAMESPACE_STD::numeric_limits::quiet_NaN()); CheckParseArithmetic(LITERAL(T, " NaN"), +NAMESPACE_STD::numeric_limits::quiet_NaN()); CheckParseArithmetic(LITERAL(T, "-NaN"), -NAMESPACE_STD::numeric_limits::quiet_NaN()); }; CheckParseFloat(InPlaceType); CheckParseFloat(InPlaceType); CheckParseFloat(InPlaceType); }; Test(InPlaceType); Test(InPlaceType); Test(InPlaceType); Test(InPlaceType); Test(InPlaceType); Test(InPlaceType); } NAMESPACE_END(Testing) NAMESPACE_MODULE_END(Utility) NAMESPACE_MODULE_END(Redcraft) NAMESPACE_REDCRAFT_END