diff --git a/Sources/Plasma/CoreLib/HeadSpin.cpp b/Sources/Plasma/CoreLib/HeadSpin.cpp index bbb46fb6..4fe5c943 100644 --- a/Sources/Plasma/CoreLib/HeadSpin.cpp +++ b/Sources/Plasma/CoreLib/HeadSpin.cpp @@ -139,8 +139,10 @@ void ErrorAssert(int line, const char* file, const char* fmt, ...) } else #endif // _MSC_VER { - char str[] = "-------\nASSERTION FAILED:\nFile: %s Line: %i\nMessage: %s\n-------"; - DebugMsg(str, file, line, msg); + DebugMsg("-------\nASSERTION FAILED:\nFile: %s Line: %i\nMessage: %s\n-------", + file, line, msg); + fflush(stderr); + DebugBreakAlways(); } #endif // HS_DEBUGGING @@ -214,17 +216,16 @@ void DebugMsg(const char* fmt, ...) va_list args; va_start(args, fmt); vsnprintf(msg, arrsize(msg), fmt, args); + fprintf(stderr, "%s\n", msg); #ifdef _MSC_VER if (DebugIsDebuggerPresent()) { + // Also print to the MSVC Output window OutputDebugStringA(msg); OutputDebugStringA("\n"); - } else -#endif - { - fprintf(stderr, "%s\n", msg); } +#endif } //////////////////////////////////////////////////////////////////////////// diff --git a/Sources/Plasma/CoreLib/plFormat.cpp b/Sources/Plasma/CoreLib/plFormat.cpp index 4126640f..cf5ebda2 100644 --- a/Sources/Plasma/CoreLib/plFormat.cpp +++ b/Sources/Plasma/CoreLib/plFormat.cpp @@ -45,6 +45,7 @@ Mead, WA 99021 #include "HeadSpin.h" #include #include +#include #define BADCHAR_REPLACEMENT (0xFFFDul) @@ -120,11 +121,10 @@ namespace plFormat_Private spec.fDigitClass = kDigitHexUpper; break; case '+': - spec.fDigitClass = kDigitDecAlwaysSigned; + spec.fAlwaysSigned = true; break; case 'd': - if (spec.fDigitClass != kDigitDecAlwaysSigned) - spec.fDigitClass = kDigitDec; + spec.fDigitClass = kDigitDec; break; case 'o': spec.fDigitClass = kDigitOct; @@ -226,7 +226,8 @@ static void _formatNumeric(const plFormat_Private::FormatSpec &format, plStringStream &output, _IType value, int radix, bool upperCase = false) { - char pad = format.fPadChar ? format.fPadChar : ' '; + static_assert(std::is_unsigned<_IType>::value, + "Signed numerics are currently only supported in Decimal formatting"); size_t format_size = 0; _IType temp = value; @@ -238,9 +239,9 @@ static void _formatNumeric(const plFormat_Private::FormatSpec &format, if (format_size == 0) format_size = 1; - hsAssert(format_size < 64, "Format length too long"); + hsAssert(format_size < 65, "Format length too long"); - char buffer[64]; + char buffer[65]; _IFormatNumeric_Impl<_IType>(buffer + format_size, value, radix, upperCase); _formatString(format, output, buffer, format_size, plFormat_Private::kAlignRight); } @@ -250,11 +251,11 @@ template static void _formatDecimal(const plFormat_Private::FormatSpec &format, plStringStream &output, _IType value) { - char pad = format.fPadChar ? format.fPadChar : ' '; - _IType abs = (value < 0) ? -value : value; + typedef typename std::make_unsigned<_IType>::type _UType; + _UType abs = (value < 0) ? -(_UType)value : value; size_t format_size = 0; - _IType temp = abs; + _UType temp = abs; while (temp) { ++format_size; temp /= 10; @@ -263,21 +264,18 @@ static void _formatDecimal(const plFormat_Private::FormatSpec &format, if (format_size == 0) format_size = 1; - if (value < 0 || format.fDigitClass == plFormat_Private::kDigitDecAlwaysSigned) + if (value < 0 || format.fAlwaysSigned) ++format_size; - hsAssert(format_size < 21, "Format length too long"); - - char buffer[21]; - _IFormatNumeric_Impl<_IType>(buffer + format_size, abs, 10); + hsAssert(format_size < 24, "Format length too long"); - int signPos = arrsize(buffer) - static_cast(format_size); - hsAssert(signPos >= 0, "Format buffer not large enough for sign"); + char buffer[24]; + _IFormatNumeric_Impl<_UType>(buffer + format_size, abs, 10); if (value < 0) - buffer[signPos] = '-'; - else if (format.fDigitClass == plFormat_Private::kDigitDecAlwaysSigned) - buffer[signPos] = '+'; + buffer[0] = '-'; + else if (format.fAlwaysSigned) + buffer[0] = '+'; _formatString(format, output, buffer, format_size, plFormat_Private::kAlignRight); } @@ -294,9 +292,7 @@ static void _formatChar(const plFormat_Private::FormatSpec &format, // Yanked from plString if (ch > 0x10FFFF) { - hsAssert(0, "Unicode character out of range"); - - // Character out of range; Use U+FFFD instead for release builds + // Character out of range; Use U+FFFD instead format_size = 3; utf8[0] = 0xE0 | ((BADCHAR_REPLACEMENT >> 12) & 0x0F); utf8[1] = 0x80 | ((BADCHAR_REPLACEMENT >> 6) & 0x3F); @@ -343,7 +339,6 @@ static void _formatChar(const plFormat_Private::FormatSpec &format, _formatNumeric<_utype>(format, output, value, 16, true); \ break; \ case plFormat_Private::kDigitDec: \ - case plFormat_Private::kDigitDecAlwaysSigned: \ case plFormat_Private::kDigitDefault: \ _formatDecimal<_stype>(format, output, value); \ break; \ @@ -372,7 +367,6 @@ static void _formatChar(const plFormat_Private::FormatSpec &format, _formatNumeric<_utype>(format, output, value, 16, true); \ break; \ case plFormat_Private::kDigitDec: \ - case plFormat_Private::kDigitDecAlwaysSigned: \ case plFormat_Private::kDigitDefault: \ _formatDecimal<_utype>(format, output, value); \ break; \ @@ -405,6 +399,10 @@ PL_FORMAT_IMPL(double) size_t end = 0; format_buffer[end++] = '%'; + + if (format.fAlwaysSigned) + format_buffer[end++] = '+'; + if (format.fPrecision >= 0) { int count = snprintf(format_buffer + end, arrsize(format_buffer) - end, ".%d", format.fPrecision); @@ -462,7 +460,6 @@ PL_FORMAT_IMPL(char) _formatNumeric(format, output, value, 16, true); break; case plFormat_Private::kDigitDec: - case plFormat_Private::kDigitDecAlwaysSigned: _formatDecimal(format, output, value); break; case plFormat_Private::kDigitChar: @@ -479,20 +476,19 @@ PL_FORMAT_IMPL(wchar_t) { switch (format.fDigitClass) { case plFormat_Private::kDigitBin: - _formatNumeric(format, output, value, 2); + _formatNumeric(format, output, value, 2); break; case plFormat_Private::kDigitOct: - _formatNumeric(format, output, value, 8); + _formatNumeric(format, output, value, 8); break; case plFormat_Private::kDigitHex: - _formatNumeric(format, output, value, 16, false); + _formatNumeric(format, output, value, 16, false); break; case plFormat_Private::kDigitHexUpper: - _formatNumeric(format, output,value, 16, true); + _formatNumeric(format, output, value, 16, true); break; case plFormat_Private::kDigitDec: - case plFormat_Private::kDigitDecAlwaysSigned: - _formatDecimal(format, output, value); + _formatDecimal(format, output, value); break; case plFormat_Private::kDigitChar: case plFormat_Private::kDigitDefault: diff --git a/Sources/Plasma/CoreLib/plFormat.h b/Sources/Plasma/CoreLib/plFormat.h index ad3cb594..7f69cdfd 100644 --- a/Sources/Plasma/CoreLib/plFormat.h +++ b/Sources/Plasma/CoreLib/plFormat.h @@ -65,7 +65,7 @@ Mead, WA 99021 * `<` | Align left * `>` | Align right * `NNN` | Pad to NNN characters (minimum - can be more) - * `+` | Show a '+' char for positive signed values (decimal only) + * `+` | Show a '+' char for positive signed values (decimal / float only) * `_C` | Use C as the pad character (only '\001'..'\177' supported for now) * `x` | Hex (lower-case) * `X` | Hex (upper-case) @@ -95,7 +95,6 @@ namespace plFormat_Private { kDigitDefault, /**< Default digit formatting */ kDigitDec, /**< Format as decimal integer */ - kDigitDecAlwaysSigned, /**< Same as `kDigitDec`, but include a '+' for positive numbers too */ kDigitHex, /**< Hex integer (assume unsigned) */ kDigitHexUpper, /**< Hex integer with upper-case digits */ kDigitOct, /**< Octal integer (assume unsigned) */ @@ -118,6 +117,7 @@ namespace plFormat_Private int fPrecision = -1; /**< Requested precision for floating-point */ char fPadChar = 0; /**< Explicit padding char (default is space) */ + bool fAlwaysSigned = false; /**< Show + for positive numbers (dec/float) */ Alignment fAlignment = kAlignDefault; /**< Requested pad alignment */ DigitClass fDigitClass = kDigitDefault; /**< Requested int formatting */ FloatClass fFloatClass = kFloatDefault; /**< Requested float formatting */ diff --git a/Sources/Plasma/CoreLib/plString.cpp b/Sources/Plasma/CoreLib/plString.cpp index 08bab171..2b706612 100644 --- a/Sources/Plasma/CoreLib/plString.cpp +++ b/Sources/Plasma/CoreLib/plString.cpp @@ -171,18 +171,24 @@ void plString::IConvertFromUtf16(const uint16_t *utf16, size_t size) plUniChar unichar = 0x10000; if (sp + 1 >= utf16 + size) { - hsAssert(0, "Incomplete surrogate pair in UTF-16 data"); + // Incomplete surrogate pair unichar = BADCHAR_REPLACEMENT; } else if (*sp < 0xDC00) { unichar += (*sp++ & 0x3FF) << 10; - hsAssert(*sp >= 0xDC00 && *sp <= 0xDFFF, - "Invalid surrogate pair in UTF-16 data"); - unichar += (*sp & 0x3FF); + if (*sp < 0xDC00 || *sp > 0xDFFF) { + // Invalid surrogate pair + unichar = BADCHAR_REPLACEMENT; + } else { + unichar += (*sp & 0x3FF); + } } else { unichar += (*sp++ & 0x3FF); - hsAssert(*sp >= 0xD800 && *sp < 0xDC00, - "Invalid surrogate pair in UTF-16 data"); - unichar += (*sp & 0x3FF) << 10; + if (*sp < 0xD800 || *sp >= 0xDC00) { + // Invalid surrogate pair + unichar = BADCHAR_REPLACEMENT; + } else { + unichar += (*sp & 0x3FF) << 10; + } } *dp++ = 0xF0 | ((unichar >> 18) & 0x07); *dp++ = 0x80 | ((unichar >> 12) & 0x3F); @@ -229,8 +235,8 @@ void plString::IConvertFromUtf32(const plUniChar *ustr, size_t size) const plUniChar *sp = ustr; while (sp < ustr + size) { if (*sp > 0x10FFFF) { - hsAssert(0, "UTF-32 character out of range"); - convlen += 3; // Use U+FFFD for release builds + // Invalid character gets replaced with U+FFFD + convlen += 3; } else if (*sp > 0xFFFF) convlen += 4; @@ -299,9 +305,9 @@ void plString::IConvertFromIso8859_1(const char *astr, size_t size) char *dp = utf8; sp = astr; while (sp < astr + size) { - if (*astr & 0x80) { - *dp++ = 0xC0 | ((*sp >> 6) & 0x1F); - *dp++ = 0x80 | ((*sp ) & 0x3F); + if (*sp & 0x80) { + *dp++ = 0xC0 | ((uint8_t(*sp) >> 6) & 0x1F); + *dp++ = 0x80 | ((uint8_t(*sp) ) & 0x3F); } else { *dp++ = *sp; } @@ -347,6 +353,7 @@ plStringBuffer plString::ToUtf16() const unichar |= (*sp++ & 0x3F) << 12; unichar |= (*sp++ & 0x3F) << 6; unichar |= (*sp++ & 0x3F); + unichar -= 0x10000; *dp++ = 0xD800 | ((unichar >> 10) & 0x3FF); *dp++ = 0xDC00 | ((unichar ) & 0x3FF); @@ -424,7 +431,7 @@ plStringBuffer plString::ToIso8859_1() const } else { unichar = *sp++; } - *dp++ = (unichar < 0xFF) ? unichar : '?'; + *dp++ = (unichar < 0x100) ? unichar : '?'; } astr[convlen] = 0; diff --git a/Sources/Tests/CoreTests/CMakeLists.txt b/Sources/Tests/CoreTests/CMakeLists.txt index c10e7796..91b300a0 100644 --- a/Sources/Tests/CoreTests/CMakeLists.txt +++ b/Sources/Tests/CoreTests/CMakeLists.txt @@ -4,6 +4,7 @@ include_directories(../../Plasma/CoreLib) SET(CoreLibTest_SOURCES test_plString.cpp + test_plFormat.cpp test_plCmdParser.cpp ) diff --git a/Sources/Tests/CoreTests/test_plFormat.cpp b/Sources/Tests/CoreTests/test_plFormat.cpp new file mode 100644 index 00000000..d0d5af1d --- /dev/null +++ b/Sources/Tests/CoreTests/test_plFormat.cpp @@ -0,0 +1,397 @@ +#include "plFormat.h" +#include "plFileSystem.h" + +#include +#include +#include + +TEST(plFormat, Escapes) +{ + EXPECT_EQ(plString("{x"), plFormat("{{{}", "x")); + EXPECT_EQ(plString("x{"), plFormat("{}{{", "x")); + EXPECT_EQ(plString("{{{{"), plFormat("{{{}{{{{", "{")); + EXPECT_EQ(plString("{xxx{{yyy{"), plFormat("{{{}{{{{{}{{", "xxx", "yyy")); +} + +TEST(plFormat, Errors) +{ + // Ensure these get printed to the console + ErrorEnableGui(false); + + EXPECT_DEATH(plFormat("{}", 1, 2), + "Message: Too many actual parameters for format string"); + EXPECT_DEATH(plFormat("{} {}", 1), + "Message: Not enough actual parameters for format string"); + EXPECT_DEATH(plFormat("{", 1), + "Message: Unterminated format specifier"); + EXPECT_DEATH(plFormat("{.", 1), + "Message: Unterminated format specifier"); + EXPECT_DEATH(plFormat("{_", 1), + "Message: Unterminated format specifier"); + EXPECT_DEATH(plFormat(nullptr, 1), + "Message: Passed a null format string"); +} + +TEST(plFormat, Strings) +{ + EXPECT_EQ(plString("TEST"), plFormat("{}", "TEST")); + EXPECT_EQ(plString("xxTESTxx"), plFormat("xx{}xx", "TEST")); + EXPECT_EQ(plString("xxTESTxx"), plFormat("xx{2}xx", "TEST")); + EXPECT_EQ(plString("xxTESTxx"), plFormat("xx{>2}xx", "TEST")); + EXPECT_EQ(plString("xxTESTxx"), plFormat("xx{<2}xx", "TEST")); + EXPECT_EQ(plString("xxTESTxx"), plFormat("xx{_-2}xx", "TEST")); + EXPECT_EQ(plString("xxTEST xx"), plFormat("xx{6}xx", "TEST")); + EXPECT_EQ(plString("xxTEST xx"), plFormat("xx{<6}xx", "TEST")); + EXPECT_EQ(plString("xx TESTxx"), plFormat("xx{>6}xx", "TEST")); + EXPECT_EQ(plString("xxTEST--xx"), plFormat("xx{_-6}xx", "TEST")); + EXPECT_EQ(plString("xxTEST--xx"), plFormat("xx{<_-6}xx", "TEST")); + EXPECT_EQ(plString("xx--TESTxx"), plFormat("xx{>_-6}xx", "TEST")); + EXPECT_EQ(plString("xxONE TWO THREExx"), plFormat("xx{5}{<5}{>7}xx", "ONE", "TWO", "THREE")); + + // Ensure braces are parsed properly within padding chars + EXPECT_EQ(plString("xxTEST}}xx"), plFormat("xx{_}6}xx", "TEST")); + EXPECT_EQ(plString("xxTEST}}xx"), plFormat("xx{6_}}xx", "TEST")); + EXPECT_EQ(plString("xxTEST{{xx"), plFormat("xx{_{6}xx", "TEST")); + EXPECT_EQ(plString("xxTEST{{xx"), plFormat("xx{6_{}xx", "TEST")); +} + +TEST(plFormat, StringClasses) +{ + // These should be handled just like normal const char* string params + // (see above), so just need to test that the wrappers are working + EXPECT_EQ(plString("xxTESTxx"), plFormat("xx{}xx", L"TEST")); + EXPECT_EQ(plString("xxTESTxx"), plFormat("xx{}xx", plString("TEST"))); + EXPECT_EQ(plString("xxTESTxx"), plFormat("xx{}xx", std::string("TEST"))); + EXPECT_EQ(plString("xxTESTxx"), plFormat("xx{}xx", std::wstring(L"TEST"))); + EXPECT_EQ(plString("xx/dev/nullxx"), plFormat("xx{}xx", plFileName("/dev/null"))); + EXPECT_EQ(plString(R"(xxC:\Users\Nobodyxx)"), + plFormat("xx{}xx", plFileName(R"(C:\Users\Nobody)"))); +} + +TEST(plFormat, Char) +{ + EXPECT_EQ(plString("xxAxx"), plFormat("xx{}xx", 'A')); + EXPECT_EQ(plString("xxAxx"), plFormat("xx{c}xx", (signed char)'A')); + EXPECT_EQ(plString("xxAxx"), plFormat("xx{c}xx", (unsigned char)'A')); + + // UTF-8 encoding of wide (16-bit) char + EXPECT_EQ(plString("xx\xef\xbf\xbexx"), plFormat("xx{}xx", L'\ufffe')); + EXPECT_EQ(plString("xx\xe7\xbf\xbexx"), plFormat("xx{c}xx", (short)0x7ffe)); + EXPECT_EQ(plString("xx\xef\xbf\xbexx"), plFormat("xx{c}xx", (unsigned short)0xfffe)); + + // UTF-8 encoding of UCS4 (32-bit) char + EXPECT_EQ(plString("xx\xf4\x8f\xbf\xbfxx"), plFormat("xx{c}xx", (int)0x10ffff)); + EXPECT_EQ(plString("xx\xf4\x8f\xbf\xbfxx"), plFormat("xx{c}xx", (unsigned int)0x10ffff)); + EXPECT_EQ(plString("xx\xf4\x8f\xbf\xbfxx"), plFormat("xx{c}xx", (long)0x10ffff)); + EXPECT_EQ(plString("xx\xf4\x8f\xbf\xbfxx"), plFormat("xx{c}xx", (unsigned long)0x10ffff)); + EXPECT_EQ(plString("xx\xf4\x8f\xbf\xbfxx"), plFormat("xx{c}xx", (int64_t)0x10ffff)); + EXPECT_EQ(plString("xx\xf4\x8f\xbf\xbfxx"), plFormat("xx{c}xx", (uint64_t)0x10ffff)); +} + +TEST(plFormat, Decimal) +{ + EXPECT_EQ(plString("xx1234xx"), plFormat("xx{}xx", 1234)); + EXPECT_EQ(plString("xx1234xx"), plFormat("xx{d}xx", 1234)); + EXPECT_EQ(plString("xx1234xx"), plFormat("xx{2}xx", 1234)); + EXPECT_EQ(plString("xx1234xx"), plFormat("xx{>2}xx", 1234)); + EXPECT_EQ(plString("xx1234xx"), plFormat("xx{<2}xx", 1234)); + EXPECT_EQ(plString("xx 1234xx"), plFormat("xx{6}xx", 1234)); + EXPECT_EQ(plString("xx 1234xx"), plFormat("xx{>6}xx", 1234)); + EXPECT_EQ(plString("xx1234 xx"), plFormat("xx{<6}xx", 1234)); + + // Character types + EXPECT_EQ(plString("xx0xx"), plFormat("xx{d}xx", '\0')); + EXPECT_EQ(plString("xx65xx"), plFormat("xx{d}xx", 'A')); + EXPECT_EQ(plString("xx0xx"), plFormat("xx{d}xx", L'\0')); + EXPECT_EQ(plString("xx65xx"), plFormat("xx{d}xx", L'A')); + EXPECT_EQ(plString("xx32767xx"), plFormat("xx{d}xx", L'\u7fff')); + + // Numeric char types + EXPECT_EQ(plString("xx0xx"), plFormat("xx{}xx", (signed char)0)); + EXPECT_EQ(plString("xx127xx"), plFormat("xx{}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx+127xx"), plFormat("xx{+}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx-128xx"), plFormat("xx{}xx", std::numeric_limits::min())); + EXPECT_EQ(plString("xx-128xx"), plFormat("xx{+}xx", std::numeric_limits::min())); + EXPECT_EQ(plString("xx0xx"), plFormat("xx{}xx", (unsigned char)0)); + EXPECT_EQ(plString("xx255xx"), plFormat("xx{}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx+255xx"), plFormat("xx{+}xx", std::numeric_limits::max())); + + // 16-bit ints + EXPECT_EQ(plString("xx0xx"), plFormat("xx{}xx", (short)0)); + EXPECT_EQ(plString("xx32767xx"), plFormat("xx{}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx+32767xx"), plFormat("xx{+}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx-32768xx"), plFormat("xx{}xx", std::numeric_limits::min())); + EXPECT_EQ(plString("xx-32768xx"), plFormat("xx{+}xx", std::numeric_limits::min())); + EXPECT_EQ(plString("xx0xx"), plFormat("xx{}xx", (unsigned short)0)); + EXPECT_EQ(plString("xx65535xx"), plFormat("xx{}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx+65535xx"), plFormat("xx{+}xx", std::numeric_limits::max())); + + // 32-bit ints + EXPECT_EQ(plString("xx0xx"), plFormat("xx{}xx", (int)0)); + EXPECT_EQ(plString("xx2147483647xx"), plFormat("xx{}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx+2147483647xx"), plFormat("xx{+}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx-2147483648xx"), plFormat("xx{}xx", std::numeric_limits::min())); + EXPECT_EQ(plString("xx-2147483648xx"), plFormat("xx{+}xx", std::numeric_limits::min())); + EXPECT_EQ(plString("xx0xx"), plFormat("xx{}xx", (unsigned int)0)); + EXPECT_EQ(plString("xx4294967295xx"), plFormat("xx{}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx+4294967295xx"), plFormat("xx{+}xx", std::numeric_limits::max())); + + // 64-bit ints + EXPECT_EQ(plString("xx0xx"), plFormat("xx{}xx", (int64_t)0)); + EXPECT_EQ(plString("xx9223372036854775807xx"), plFormat("xx{}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx+9223372036854775807xx"), plFormat("xx{+}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx-9223372036854775808xx"), plFormat("xx{}xx", std::numeric_limits::min())); + EXPECT_EQ(plString("xx-9223372036854775808xx"), plFormat("xx{+}xx", std::numeric_limits::min())); + EXPECT_EQ(plString("xx0xx"), plFormat("xx{}xx", (uint64_t)0)); + EXPECT_EQ(plString("xx18446744073709551615xx"), plFormat("xx{}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx+18446744073709551615xx"), plFormat("xx{+}xx", std::numeric_limits::max())); +} + +TEST(plFormat, Hex) +{ + EXPECT_EQ(plString("xx4d2xx"), plFormat("xx{x}xx", 1234)); + EXPECT_EQ(plString("xx4d2xx"), plFormat("xx{x}xx", 1234)); + EXPECT_EQ(plString("xx4d2xx"), plFormat("xx{2x}xx", 1234)); + EXPECT_EQ(plString("xx4d2xx"), plFormat("xx{>2x}xx", 1234)); + EXPECT_EQ(plString("xx4d2xx"), plFormat("xx{<2x}xx", 1234)); + EXPECT_EQ(plString("xx 4d2xx"), plFormat("xx{6x}xx", 1234)); + EXPECT_EQ(plString("xx 4d2xx"), plFormat("xx{>6x}xx", 1234)); + EXPECT_EQ(plString("xx4d2 xx"), plFormat("xx{<6x}xx", 1234)); + + // Character types + EXPECT_EQ(plString("xx0xx"), plFormat("xx{x}xx", '\0')); + EXPECT_EQ(plString("xx41xx"), plFormat("xx{x}xx", 'A')); + EXPECT_EQ(plString("xx0xx"), plFormat("xx{x}xx", L'\0')); + EXPECT_EQ(plString("xx41xx"), plFormat("xx{x}xx", L'A')); + EXPECT_EQ(plString("xx7fffxx"), plFormat("xx{x}xx", L'\u7fff')); + + // Numeric char types + EXPECT_EQ(plString("xx0xx"), plFormat("xx{x}xx", (signed char)0)); + EXPECT_EQ(plString("xx7fxx"), plFormat("xx{x}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx80xx"), plFormat("xx{x}xx", std::numeric_limits::min())); + EXPECT_EQ(plString("xx0xx"), plFormat("xx{x}xx", (unsigned char)0)); + EXPECT_EQ(plString("xxffxx"), plFormat("xx{x}xx", std::numeric_limits::max())); + + // 16-bit ints + EXPECT_EQ(plString("xx0xx"), plFormat("xx{x}xx", (short)0)); + EXPECT_EQ(plString("xx7fffxx"), plFormat("xx{x}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx8000xx"), plFormat("xx{x}xx", std::numeric_limits::min())); + EXPECT_EQ(plString("xx0xx"), plFormat("xx{x}xx", (unsigned short)0)); + EXPECT_EQ(plString("xxffffxx"), plFormat("xx{x}xx", std::numeric_limits::max())); + + // 32-bit ints + EXPECT_EQ(plString("xx0xx"), plFormat("xx{x}xx", (int)0)); + EXPECT_EQ(plString("xx7fffffffxx"), plFormat("xx{x}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx80000000xx"), plFormat("xx{x}xx", std::numeric_limits::min())); + EXPECT_EQ(plString("xx0xx"), plFormat("xx{x}xx", (unsigned int)0)); + EXPECT_EQ(plString("xxffffffffxx"), plFormat("xx{x}xx", std::numeric_limits::max())); + + // 64-bit ints + EXPECT_EQ(plString("xx0xx"), plFormat("xx{x}xx", (int64_t)0)); + EXPECT_EQ(plString("xx7fffffffffffffffxx"), plFormat("xx{x}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx8000000000000000xx"), plFormat("xx{x}xx", std::numeric_limits::min())); + EXPECT_EQ(plString("xx0xx"), plFormat("xx{x}xx", (uint64_t)0)); + EXPECT_EQ(plString("xxffffffffffffffffxx"), plFormat("xx{x}xx", std::numeric_limits::max())); +} + +TEST(plFormat, HexUpper) +{ + EXPECT_EQ(plString("xx4D2xx"), plFormat("xx{X}xx", 1234)); + EXPECT_EQ(plString("xx4D2xx"), plFormat("xx{X}xx", 1234)); + EXPECT_EQ(plString("xx4D2xx"), plFormat("xx{2X}xx", 1234)); + EXPECT_EQ(plString("xx4D2xx"), plFormat("xx{>2X}xx", 1234)); + EXPECT_EQ(plString("xx4D2xx"), plFormat("xx{<2X}xx", 1234)); + EXPECT_EQ(plString("xx 4D2xx"), plFormat("xx{6X}xx", 1234)); + EXPECT_EQ(plString("xx 4D2xx"), plFormat("xx{>6X}xx", 1234)); + EXPECT_EQ(plString("xx4D2 xx"), plFormat("xx{<6X}xx", 1234)); + + // Character types + EXPECT_EQ(plString("xx0xx"), plFormat("xx{X}xx", '\0')); + EXPECT_EQ(plString("xx41xx"), plFormat("xx{X}xx", 'A')); + EXPECT_EQ(plString("xx0xx"), plFormat("xx{X}xx", L'\0')); + EXPECT_EQ(plString("xx41xx"), plFormat("xx{X}xx", L'A')); + EXPECT_EQ(plString("xx7FFFxx"), plFormat("xx{X}xx", L'\u7fff')); + + // Numeric char types + EXPECT_EQ(plString("xx0xx"), plFormat("xx{X}xx", (signed char)0)); + EXPECT_EQ(plString("xx7Fxx"), plFormat("xx{X}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx80xx"), plFormat("xx{X}xx", std::numeric_limits::min())); + EXPECT_EQ(plString("xx0xx"), plFormat("xx{X}xx", (unsigned char)0)); + EXPECT_EQ(plString("xxFFxx"), plFormat("xx{X}xx", std::numeric_limits::max())); + + // 16-bit ints + EXPECT_EQ(plString("xx0xx"), plFormat("xx{X}xx", (short)0)); + EXPECT_EQ(plString("xx7FFFxx"), plFormat("xx{X}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx8000xx"), plFormat("xx{X}xx", std::numeric_limits::min())); + EXPECT_EQ(plString("xx0xx"), plFormat("xx{X}xx", (unsigned short)0)); + EXPECT_EQ(plString("xxFFFFxx"), plFormat("xx{X}xx", std::numeric_limits::max())); + + // 32-bit ints + EXPECT_EQ(plString("xx0xx"), plFormat("xx{X}xx", (int)0)); + EXPECT_EQ(plString("xx7FFFFFFFxx"), plFormat("xx{X}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx80000000xx"), plFormat("xx{X}xx", std::numeric_limits::min())); + EXPECT_EQ(plString("xx0xx"), plFormat("xx{X}xx", (unsigned int)0)); + EXPECT_EQ(plString("xxFFFFFFFFxx"), plFormat("xx{X}xx", std::numeric_limits::max())); + + // 64-bit ints + EXPECT_EQ(plString("xx0xx"), plFormat("xx{X}xx", (int64_t)0)); + EXPECT_EQ(plString("xx7FFFFFFFFFFFFFFFxx"), plFormat("xx{X}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx8000000000000000xx"), plFormat("xx{X}xx", std::numeric_limits::min())); + EXPECT_EQ(plString("xx0xx"), plFormat("xx{X}xx", (uint64_t)0)); + EXPECT_EQ(plString("xxFFFFFFFFFFFFFFFFxx"), plFormat("xx{X}xx", std::numeric_limits::max())); +} + +TEST(plFormat, Octal) +{ + EXPECT_EQ(plString("xx2322xx"), plFormat("xx{o}xx", 1234)); + EXPECT_EQ(plString("xx2322xx"), plFormat("xx{o}xx", 1234)); + EXPECT_EQ(plString("xx2322xx"), plFormat("xx{2o}xx", 1234)); + EXPECT_EQ(plString("xx2322xx"), plFormat("xx{>2o}xx", 1234)); + EXPECT_EQ(plString("xx2322xx"), plFormat("xx{<2o}xx", 1234)); + EXPECT_EQ(plString("xx 2322xx"), plFormat("xx{6o}xx", 1234)); + EXPECT_EQ(plString("xx 2322xx"), plFormat("xx{>6o}xx", 1234)); + EXPECT_EQ(plString("xx2322 xx"), plFormat("xx{<6o}xx", 1234)); + + // Character types + EXPECT_EQ(plString("xx0xx"), plFormat("xx{o}xx", '\0')); + EXPECT_EQ(plString("xx101xx"), plFormat("xx{o}xx", 'A')); + EXPECT_EQ(plString("xx0xx"), plFormat("xx{o}xx", L'\0')); + EXPECT_EQ(plString("xx101xx"), plFormat("xx{o}xx", L'A')); + EXPECT_EQ(plString("xx77777xx"), plFormat("xx{o}xx", L'\u7fff')); + + // Numeric char types + EXPECT_EQ(plString("xx0xx"), plFormat("xx{o}xx", (signed char)0)); + EXPECT_EQ(plString("xx177xx"), plFormat("xx{o}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx200xx"), plFormat("xx{o}xx", std::numeric_limits::min())); + EXPECT_EQ(plString("xx0xx"), plFormat("xx{o}xx", (unsigned char)0)); + EXPECT_EQ(plString("xx377xx"), plFormat("xx{o}xx", std::numeric_limits::max())); + + // 16-bit ints + EXPECT_EQ(plString("xx0xx"), plFormat("xx{o}xx", (short)0)); + EXPECT_EQ(plString("xx77777xx"), plFormat("xx{o}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx100000xx"), plFormat("xx{o}xx", std::numeric_limits::min())); + EXPECT_EQ(plString("xx0xx"), plFormat("xx{o}xx", (unsigned short)0)); + EXPECT_EQ(plString("xx177777xx"), plFormat("xx{o}xx", std::numeric_limits::max())); + + // 32-bit ints + EXPECT_EQ(plString("xx0xx"), plFormat("xx{o}xx", (int)0)); + EXPECT_EQ(plString("xx17777777777xx"), plFormat("xx{o}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx20000000000xx"), plFormat("xx{o}xx", std::numeric_limits::min())); + EXPECT_EQ(plString("xx0xx"), plFormat("xx{o}xx", (unsigned int)0)); + EXPECT_EQ(plString("xx37777777777xx"), plFormat("xx{o}xx", std::numeric_limits::max())); + + // 64-bit ints + EXPECT_EQ(plString("xx0xx"), plFormat("xx{o}xx", (int64_t)0)); + EXPECT_EQ(plString("xx777777777777777777777xx"), plFormat("xx{o}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx1000000000000000000000xx"), plFormat("xx{o}xx", std::numeric_limits::min())); + EXPECT_EQ(plString("xx0xx"), plFormat("xx{o}xx", (uint64_t)0)); + EXPECT_EQ(plString("xx1777777777777777777777xx"), plFormat("xx{o}xx", std::numeric_limits::max())); +} + +TEST(plFormat, Binary) +{ + EXPECT_EQ(plString("xx10011010010xx"), plFormat("xx{b}xx", 1234)); + EXPECT_EQ(plString("xx10011010010xx"), plFormat("xx{b}xx", 1234)); + EXPECT_EQ(plString("xx10011010010xx"), plFormat("xx{2b}xx", 1234)); + EXPECT_EQ(plString("xx10011010010xx"), plFormat("xx{>2b}xx", 1234)); + EXPECT_EQ(plString("xx10011010010xx"), plFormat("xx{<2b}xx", 1234)); + EXPECT_EQ(plString("xx 10011010010xx"), plFormat("xx{16b}xx", 1234)); + EXPECT_EQ(plString("xx 10011010010xx"), plFormat("xx{>16b}xx", 1234)); + EXPECT_EQ(plString("xx10011010010 xx"), plFormat("xx{<16b}xx", 1234)); + + // Character types + EXPECT_EQ(plString("xx0xx"), plFormat("xx{b}xx", '\0')); + EXPECT_EQ(plString("xx1000001xx"), plFormat("xx{b}xx", 'A')); + EXPECT_EQ(plString("xx0xx"), plFormat("xx{b}xx", L'\0')); + EXPECT_EQ(plString("xx1000001xx"), plFormat("xx{b}xx", L'A')); + EXPECT_EQ(plString("xx111111111111111xx"), plFormat("xx{b}xx", L'\u7fff')); + + // Numeric char types + EXPECT_EQ(plString("xx0xx"), plFormat("xx{b}xx", (signed char)0)); + EXPECT_EQ(plString("xx1111111xx"), plFormat("xx{b}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx10000000xx"), plFormat("xx{b}xx", std::numeric_limits::min())); + EXPECT_EQ(plString("xx0xx"), plFormat("xx{b}xx", (unsigned char)0)); + EXPECT_EQ(plString("xx11111111xx"), plFormat("xx{b}xx", std::numeric_limits::max())); + + // 16-bit ints + EXPECT_EQ(plString("xx0xx"), plFormat("xx{b}xx", (short)0)); + EXPECT_EQ(plString("xx111111111111111xx"), plFormat("xx{b}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx1000000000000000xx"), plFormat("xx{b}xx", std::numeric_limits::min())); + EXPECT_EQ(plString("xx0xx"), plFormat("xx{b}xx", (unsigned short)0)); + EXPECT_EQ(plString("xx1111111111111111xx"), plFormat("xx{b}xx", std::numeric_limits::max())); + + // 32-bit ints + EXPECT_EQ(plString("xx0xx"), plFormat("xx{b}xx", (int)0)); + EXPECT_EQ(plString("xx1111111111111111111111111111111xx"), + plFormat("xx{b}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx10000000000000000000000000000000xx"), + plFormat("xx{b}xx", std::numeric_limits::min())); + EXPECT_EQ(plString("xx0xx"), plFormat("xx{b}xx", (unsigned int)0)); + EXPECT_EQ(plString("xx11111111111111111111111111111111xx"), + plFormat("xx{b}xx", std::numeric_limits::max())); + + // 64-bit ints + EXPECT_EQ(plString("xx0xx"), plFormat("xx{b}xx", (int64_t)0)); + EXPECT_EQ(plString("xx111111111111111111111111111111111111111111111111111111111111111xx"), + plFormat("xx{b}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx1000000000000000000000000000000000000000000000000000000000000000xx"), + plFormat("xx{b}xx", std::numeric_limits::min())); + EXPECT_EQ(plString("xx0xx"), plFormat("xx{b}xx", (uint64_t)0)); + EXPECT_EQ(plString("xx1111111111111111111111111111111111111111111111111111111111111111xx"), + plFormat("xx{b}xx", std::numeric_limits::max())); +} + +TEST(plFormat, FloatingPoint) +{ + // The actual formatting is handled by libc, so we just need to test + // that the flags get passed along properly. + + EXPECT_EQ(plString("xx1.5xx"), plFormat("xx{}xx", 1.5)); + EXPECT_EQ(plString("xx+1.5xx"), plFormat("xx{+}xx", 1.5)); + EXPECT_EQ(plString("xx-1.5xx"), plFormat("xx{}xx", -1.5)); + EXPECT_EQ(plString("xx-1.5xx"), plFormat("xx{+}xx", -1.5)); + + // Padding + EXPECT_EQ(plString("xx 1.50xx"), plFormat("xx{6.2f}xx", 1.5)); + EXPECT_EQ(plString("xx -1.50xx"), plFormat("xx{6.2f}xx", -1.5)); + EXPECT_EQ(plString("xx1.50 xx"), plFormat("xx{<6.2f}xx", 1.5)); + EXPECT_EQ(plString("xx-1.50 xx"), plFormat("xx{<6.2f}xx", -1.5)); + + // Fixed notation + EXPECT_EQ(plString("xx3.14xx"), plFormat("xx{.2f}xx", 3.14159)); + EXPECT_EQ(plString("xx3.141590xx"), plFormat("xx{.6f}xx", 3.14159)); + EXPECT_EQ(plString("xx16384.00xx"), plFormat("xx{.2f}xx", 16384.0)); + EXPECT_EQ(plString("xx0.01xx"), plFormat("xx{.2f}xx", 1.0 / 128)); + + // MSVC uses 3 digits for the exponent, whereas GCC uses two :/ +#ifdef _MSC_VER +# define EXTRA_DIGIT "0" +#else +# define EXTRA_DIGIT "" +#endif + + // Scientific notation (MSVC uses 3 digits for the exponent) + EXPECT_EQ(plString("xx3.14e+" EXTRA_DIGIT "00xx"), plFormat("xx{.2e}xx", 3.14159)); + EXPECT_EQ(plString("xx3.141590e+" EXTRA_DIGIT "00xx"), plFormat("xx{.6e}xx", 3.14159)); + EXPECT_EQ(plString("xx1.64e+" EXTRA_DIGIT "04xx"), plFormat("xx{.2e}xx", 16384.0)); + EXPECT_EQ(plString("xx7.81e-" EXTRA_DIGIT "03xx"), plFormat("xx{.2e}xx", 1.0 / 128)); + + // Scientific notation (upper-case E) + EXPECT_EQ(plString("xx3.14E+" EXTRA_DIGIT "00xx"), plFormat("xx{.2E}xx", 3.14159)); + EXPECT_EQ(plString("xx3.141590E+" EXTRA_DIGIT "00xx"), plFormat("xx{.6E}xx", 3.14159)); + EXPECT_EQ(plString("xx1.64E+" EXTRA_DIGIT "04xx"), plFormat("xx{.2E}xx", 16384.0)); + EXPECT_EQ(plString("xx7.81E-" EXTRA_DIGIT "03xx"), plFormat("xx{.2E}xx", 1.0 / 128)); + + // Automatic (based on input) + EXPECT_EQ(plString("xx3.1xx"), plFormat("xx{.2}xx", 3.14159)); + EXPECT_EQ(plString("xx3.14159xx"), plFormat("xx{.6}xx", 3.14159)); + EXPECT_EQ(plString("xx1.6e+" EXTRA_DIGIT "04xx"), plFormat("xx{.2}xx", 16384.0)); + EXPECT_EQ(plString("xx0.0078xx"), plFormat("xx{.2}xx", 1.0 / 128)); +} + +TEST(plFormat, Booleans) +{ + // This basically just uses the string formatter with constant strings + EXPECT_EQ(plString("xxtrue xx"), plFormat("xx{5}xx", true)); + EXPECT_EQ(plString("xxfalsexx"), plFormat("xx{5}xx", false)); +} diff --git a/Sources/Tests/CoreTests/test_plString.cpp b/Sources/Tests/CoreTests/test_plString.cpp index f9cf0566..970e2522 100644 --- a/Sources/Tests/CoreTests/test_plString.cpp +++ b/Sources/Tests/CoreTests/test_plString.cpp @@ -1,278 +1,629 @@ -#include -#include +#include "plString.h" #include #include - -TEST(PlStringTest,ToUtf16) +static const plUniChar test_data[] = { + 0x20, 0x7f, /* Normal ASCII chars */ + 0xff, 0x100, /* 8-bit boundary chars */ + 0x7fff, /* UTF-8 2-byte boundary */ + 0xffff, 0x10000, /* 16-bit boundary chars */ + 0x10020, 0x40000, /* Non-edge UTF-16 surrogate pairs */ + 0x10ffff, /* Highest Unicode character */ + 0 /* Null terminator */ +}; + +/* UTF-8 version of above test data */ +static const char utf8_test_data[] = + "\x20" "\x7f" + "\xc3\xbf" "\xc4\x80" + "\xe7\xbf\xbf" + "\xef\xbf\xbf" "\xf0\x90\x80\x80" + "\xf0\x90\x80\xa0" "\xf1\x80\x80\x80" + "\xf4\x8f\xbf\xbf"; +static const size_t utf8_test_data_length = arrsize(utf8_test_data) - 1; + +/* UTF-16 version of test data */ +static const uint16_t utf16_test_data[] = { + 0x20, 0x7f, + 0xff, 0x100, + 0x7fff, + 0xffff, + /* surrogate pairs for chars >0xffff */ + 0xd800, 0xdc00, + 0xd800, 0xdc20, + 0xd8c0, 0xdc00, + 0xdbff, 0xdfff, + 0 +}; + +/* Utility for comparing plUniChar buffers */ +template +static int T_strcmp(const _Ch *left, const _Ch *right) { - uint16_t text[] = {0x0061,0x0062,0x0063,0x0064}; //abcd as in utf16 - plStringBuffer expected = plStringBuffer(text,arrsize(text)); - plStringBuffer output = plString("abcd").ToUtf16(); - - EXPECT_EQ(expected.GetSize(), output.GetSize()); //not really a good test + for ( ;; ) { + if (*left != *right) + return *left - *right; + if (*left == 0) + return (*right == 0) ? 0 : -1; + if (*right == 0) + return 1; + + ++left; + ++right; + } } -TEST(PlStringTest,ToWchar) +TEST(plString, TestHelpers) { - wchar_t text[] =L"abcd\u00E9"; - plStringBuffer expected = plStringBuffer(text,arrsize(text)); - plStringBuffer output = plString("abcd\xC3\xA9").ToWchar(); - EXPECT_STREQ(expected.GetData(),output.GetData()); + /* Ensure the utilities for testing the module function properly */ + EXPECT_EQ(0, T_strcmp("abc", "abc")); + EXPECT_LT(0, T_strcmp("abc", "aba")); + EXPECT_GT(0, T_strcmp("abc", "abe")); + EXPECT_LT(0, T_strcmp("abc", "ab")); + EXPECT_GT(0, T_strcmp("abc", "abcd")); + EXPECT_EQ(0, T_strcmp("", "")); + EXPECT_GT(0, T_strcmp("", "a")); + EXPECT_LT(0, T_strcmp("a", "")); } -TEST(PlStringTest,ToIso8859_1) +TEST(plString, Utility) { - char text[] ="abcde"; - plStringBuffer expected = plStringBuffer(text,arrsize(text)); - plStringBuffer output = plString("abcde").ToIso8859_1(); - EXPECT_STREQ(expected.GetData(),output.GetData()); + // Special plString::Null constant + EXPECT_EQ(plString::Null, plString("")); + EXPECT_EQ(plString::Null, plString{}); + + EXPECT_EQ(0, plString::Null.GetSize()); + EXPECT_EQ(true, plString::Null.IsEmpty()); + EXPECT_EQ(true, plString::Null.IsNull()); + + // Short and Long string length + EXPECT_EQ(4, plString("1234").GetSize()); + EXPECT_EQ(32, plString("12345678901234567890123456789012").GetSize()); + + // plStrings store data as UTF-8 internally + EXPECT_EQ(utf8_test_data_length, plString(utf8_test_data).GetSize()); } -TEST(PlStringTest,FindChar) +TEST(plString, StackStrings) { - plString input = plString("abCdcBAeab"); - int result=0; - //available char, case sensitive - result = input.Find('B',plString::kCaseSensitive); - EXPECT_EQ(5,result); - - //available char, case insensitive - result = input.Find('B',plString::kCaseInsensitive); - EXPECT_EQ(1,result); - - //unavailable char, case sensitive - result = input.Find('f',plString::kCaseSensitive); - EXPECT_EQ(-1,result); - - //unavailable char, case insensitive - result=0; - result = input.Find('f',plString::kCaseInsensitive); - EXPECT_EQ(-1,result); - - plString input1 = plString("abCdcBÁèab"); - //available accented char, case sensitive - result = input1.Find('Á',plString::kCaseSensitive); - EXPECT_EQ(7,result); - - //available accented char, case insensitive - result = input1.Find('è',plString::kCaseInsensitive); - EXPECT_EQ(9,result); + char stack_buf[256]; + strcpy(stack_buf, "Test"); + plString test(stack_buf); + + EXPECT_EQ(plString("Test"), test); + EXPECT_EQ(strlen("Test"), test.GetSize()); + + strcpy(stack_buf, "operator="); + test = stack_buf; + + EXPECT_EQ(plString("operator="), test); + EXPECT_EQ(strlen("operator="), test.GetSize()); } -TEST(PlStringTest,FindLast) +TEST(plString, ConvertUtf8) { - plString input = plString("abCdcBAeab"); - int result=0; - //available char, case sensitive - result = input.FindLast('B',plString::kCaseSensitive); - EXPECT_EQ(5,result); - - //available char, case insensitive - result = input.FindLast('B',plString::kCaseInsensitive); - EXPECT_EQ(9,result); - - //unavailable char, case sensitive - result = input.FindLast('f',plString::kCaseSensitive); - EXPECT_EQ(-1,result); - - //unavailable char, case insensitive - result=0; - result = input.FindLast('f',plString::kCaseInsensitive); - EXPECT_EQ(-1,result); - - plString input1 = plString("éeÉß"); - //available accented char, case sensitive - result = input1.FindLast('e',plString::kCaseSensitive); - EXPECT_EQ(2,result); - - //available accented char, case insensitive - result = input1.FindLast('ß',plString::kCaseInsensitive); - EXPECT_EQ(6,result); + // From UTF-8 to plString + plString from_utf8 = plString::FromUtf8(utf8_test_data); + EXPECT_STREQ(utf8_test_data, from_utf8.c_str()); + EXPECT_EQ(utf8_test_data_length, from_utf8.GetSize()); + plUnicodeBuffer unicode = from_utf8.GetUnicodeArray(); + EXPECT_EQ(0, T_strcmp(test_data, unicode.GetData())); + + // From plString to UTF-8 + plString to_utf8 = plString::FromUtf32(test_data); + EXPECT_STREQ(utf8_test_data, to_utf8.c_str()); + + // Empty strings + plString empty = plString::FromUtf8(""); + EXPECT_EQ(0, empty.GetSize()); + EXPECT_EQ(0, T_strcmp(empty.c_str(), "")); + + const plUniChar empty_data[] = { 0 }; + empty = plString::FromUtf32(empty_data); + EXPECT_EQ(0, empty.GetSize()); + EXPECT_EQ(0, T_strcmp(empty.c_str(), "")); } -TEST(PlStringTest,FindString) +TEST(plString, ConvertUtf16) { - plString input = plString("abABÁè"); - int result=0; - //available string, case sensitive - result = input.Find("AB",plString::kCaseSensitive); - EXPECT_EQ(2,result); - - //available string, case insensitive - result = input.Find("ab",plString::kCaseInsensitive); - EXPECT_EQ(0,result); - - //unavailable string, case sensitive - result = input.Find("cd",plString::kCaseSensitive); - EXPECT_EQ(-1,result); - - //unavailable string, case insensitive - result=0; - result = input.Find("cd",plString::kCaseInsensitive); - EXPECT_EQ(-1,result); - - plString input1 = plString("àbéCdcBÀéab"); - //available accented string, case sensitive - result = input1.Find("À",plString::kCaseSensitive); - EXPECT_EQ(9,result); - - //the strnicmp method used does not support unicode - //available accented string, case insensitive - // result = input1.Find("À",plString::kCaseInsensitive); - // EXPECT_EQ(1,result); + // From UTF-16 to plString + plString from_utf16 = plString::FromUtf16(utf16_test_data); + EXPECT_EQ(utf8_test_data_length, from_utf16.GetSize()); + plUnicodeBuffer unicode = from_utf16.GetUnicodeArray(); + EXPECT_EQ(0, T_strcmp(test_data, unicode.GetData())); + + // From plString to UTF-16 + plStringBuffer to_utf16 = plString::FromUtf32(test_data).ToUtf16(); + EXPECT_EQ(0, T_strcmp(utf16_test_data, to_utf16.GetData())); + + // Empty string + const uint16_t empty_data[] = { 0 }; + plString empty = plString::FromUtf16(empty_data); + EXPECT_EQ(0, empty.GetSize()); + EXPECT_EQ(0, T_strcmp(empty.c_str(), "")); } -//TODO: test regex functions - -TEST(PlStringTest,TrimLeft) +TEST(plString, ConvertIso8859_1) { - plString input = plString("abcdefgh"); - plString output = input.TrimLeft("abc"); - plString expected = plString("defgh"); - EXPECT_EQ(expected,output); - - plString input1 = plString("abcdefgh"); - plString output1 = input1.TrimLeft("bc"); - EXPECT_EQ(input1,output1); + // From ISO-8859-1 to plString + const char latin1[] = "\x20\x7e\xa0\xff"; + const plUniChar unicode_cp0[] = { 0x20, 0x7e, 0xa0, 0xff, 0 }; + static const size_t latin1_utf8_length = 6; + plString from_latin1 = plString::FromIso8859_1(latin1); + EXPECT_EQ(latin1_utf8_length, from_latin1.GetSize()); + plUnicodeBuffer unicode = from_latin1.GetUnicodeArray(); + EXPECT_EQ(0, T_strcmp(unicode_cp0, unicode.GetData())); + + // From plString to ISO-8859-1 + plStringBuffer to_latin1 = plString::FromUtf32(unicode_cp0).ToIso8859_1(); + EXPECT_STREQ(latin1, to_latin1.GetData()); + + // Empty string + plString empty = plString::FromIso8859_1(""); + EXPECT_EQ(0, empty.GetSize()); + EXPECT_EQ(0, T_strcmp(empty.c_str(), "")); } -TEST(PlStringTest,TrimRight) +TEST(plString, ConvertWchar) { - plString input = plString("abcdefgh"); - plString output = input.TrimRight("fgh"); - plString expected = plString("abcde"); - EXPECT_EQ(expected,output); - - plString input1 = plString("abcdefgh"); - plString output1 = input1.TrimRight("fg"); - EXPECT_EQ(input1,output1); + // UTF-8 and UTF-16 are already tested, so just make sure we test + // wchar_t and L"" conversions + + const wchar_t wtext[] = L"\x20\x7f\xff\u0100\uffff"; + const plUniChar unicode_text[] = { 0x20, 0x7f, 0xff, 0x100, 0xffff, 0 }; + static const size_t wtext_utf8_length = 9; + plString from_wchar = plString::FromWchar(wtext); + EXPECT_EQ(wtext_utf8_length, from_wchar.GetSize()); + plUnicodeBuffer unicode = from_wchar.GetUnicodeArray(); + EXPECT_EQ(0, T_strcmp(unicode_text, unicode.GetData())); + + // From plString to wchar_t + plStringBuffer to_wchar = plString::FromUtf32(unicode_text).ToWchar(); + EXPECT_STREQ(wtext, to_wchar.GetData()); + + // Empty string + plString empty = plString::FromWchar(L""); + EXPECT_EQ(0, empty.GetSize()); + EXPECT_EQ(0, T_strcmp(empty.c_str(), "")); } -TEST(PlStringTest,Trim) +TEST(plString, ConvertInvalid) { - plString input = plString("abcdefba"); - plString output = input.Trim("ab"); - plString expected = plString("cdef"); - EXPECT_EQ(expected,output); - - plString input1 = plString("abcdefba"); - plString output1 = input1.Trim("f"); - EXPECT_EQ(input1,output1); + // The following should encode replacement characters for invalid chars + const plUniChar unicode_replacement[] = { 0xfffd, 0 }; + const char latin1_replacement[] = "?"; + + // Character outside of Unicode specification range + const plUniChar too_big_c[] = { 0xffffff, 0 }; + plUnicodeBuffer too_big = plString::FromUtf32(too_big_c).GetUnicodeArray(); + EXPECT_EQ(0, T_strcmp(unicode_replacement, too_big.GetData())); + + // Invalid surrogate pairs + const uint16_t incomplete_surr_c[] = { 0xd800, 0 }; + plString incomplete_surr = plString::FromUtf16(incomplete_surr_c); + EXPECT_EQ(0, T_strcmp(unicode_replacement, + incomplete_surr.GetUnicodeArray().GetData())); + + const uint16_t double_low_c[] = { 0xd800, 0xd801, 0 }; + plString double_low = plString::FromUtf16(double_low_c); + EXPECT_EQ(0, T_strcmp(unicode_replacement, double_low.GetUnicodeArray().GetData())); + + const uint16_t bad_combo_c[] = { 0xdc00, 0x20, 0 }; + plString bad_combo = plString::FromUtf16(double_low_c); + EXPECT_EQ(0, T_strcmp(unicode_replacement, bad_combo.GetUnicodeArray().GetData())); + + // ISO-8859-1 doesn't have \ufffd, so it uses '?' instead + const plUniChar non_latin1_c[] = { 0x1ff, 0 }; + plStringBuffer non_latin1 = plString::FromUtf32(non_latin1_c).ToIso8859_1(); + EXPECT_STREQ(latin1_replacement, non_latin1.GetData()); } -TEST(PlStringTest,Substr) +TEST(plString, Concatenation) { - plString input = plString("abcdefgh"); - - //start > size returns null - plString output = input.Substr(15,1); - EXPECT_EQ(plString::Null,output); - - //start<0 - plString output1 =input.Substr(-3,3); - plString expected1 = plString("fgh"); - EXPECT_EQ(expected1,output1); - - //start+size>size string - plString output2 =input.Substr(4,6); - plString expected2 = plString("efgh"); - EXPECT_EQ(expected2,output2); - - //start =0 size = length string - plString output3 =input.Substr(0,input.GetSize()); - EXPECT_EQ(input,output3); - - //normal case - plString output4 =input.Substr(1,3); - plString expected4 = plString("bcd"); - EXPECT_EQ(expected4,output4); + // If this changes, this test may need to be updated to match + ASSERT_EQ(16, SSO_CHARS); + + plString expected_short = "xxxxyyy"; + plString input1 = "xxxx"; + plString input2 = "yyy"; + + plString expected_long = "xxxxxxxxxxyyyyyyyyy"; + plString input3 = "xxxxxxxxxx"; + plString input4 = "yyyyyyyyy"; + + // plString + plString + EXPECT_EQ(expected_short, input1 + input2); + EXPECT_EQ(expected_long, input3 + input4); + EXPECT_EQ(input1, input1 + plString()); + EXPECT_EQ(input1, plString() + input1); + + // plstring + const char* + EXPECT_EQ(expected_short, input1 + input2.c_str()); + EXPECT_EQ(expected_short, input1.c_str() + input2); + EXPECT_EQ(expected_long, input3 + input4.c_str()); + EXPECT_EQ(expected_long, input3.c_str() + input4); + EXPECT_EQ(input1, input1 + ""); + EXPECT_EQ(input1, "" + input1); } -TEST(PlStringTest,Replace) +TEST(plString, Compare) { - plString input = plString("abcdabcd"); - - plString output = input.Replace("ab","cd"); - plString expected = plString("cdcdcdcd"); - EXPECT_EQ(expected,output); - - plString output1 = input.Replace("a","cd"); - plString expected1 = plString("cdbcdcdbcd"); - EXPECT_EQ(expected1,output1); + // Same length, case sensitive + EXPECT_EQ(0, plString("abc").Compare("abc", plString::kCaseSensitive)); + EXPECT_GT(0, plString("abc").Compare("abd", plString::kCaseSensitive)); + EXPECT_LT(0, plString("abc").Compare("abb", plString::kCaseSensitive)); + EXPECT_GT(0, plString("abC").Compare("abc", plString::kCaseSensitive)); + EXPECT_GT(0, plString("Abc").Compare("abc", plString::kCaseSensitive)); + EXPECT_EQ(0, plString().Compare("", plString::kCaseSensitive)); + + // Same length, case insensitive + EXPECT_EQ(0, plString("abc").Compare("abc", plString::kCaseInsensitive)); + EXPECT_EQ(0, plString("abc").Compare("ABC", plString::kCaseInsensitive)); + EXPECT_GT(0, plString("abc").Compare("abD", plString::kCaseInsensitive)); + EXPECT_LT(0, plString("abc").Compare("abB", plString::kCaseInsensitive)); + EXPECT_EQ(0, plString().Compare("", plString::kCaseInsensitive)); + + // Mismatched length, case sensitive + EXPECT_LT(0, plString("abc").Compare("ab", plString::kCaseSensitive)); + EXPECT_GT(0, plString("abc").Compare("abcd", plString::kCaseSensitive)); + EXPECT_LT(0, plString("abc").Compare("", plString::kCaseSensitive)); + EXPECT_GT(0, plString("").Compare("abc", plString::kCaseSensitive)); + + // Mismatched length, case insensitive + EXPECT_LT(0, plString("abc").Compare("Ab", plString::kCaseInsensitive)); + EXPECT_GT(0, plString("abc").Compare("Abcd", plString::kCaseInsensitive)); + EXPECT_LT(0, plString("abc").Compare("", plString::kCaseInsensitive)); + EXPECT_GT(0, plString().Compare("abc", plString::kCaseInsensitive)); +} +TEST(plString, CompareN) +{ + // Same length, case sensitive + EXPECT_EQ(0, plString("abcXX").CompareN("abcYY", 3, plString::kCaseSensitive)); + EXPECT_GT(0, plString("abcXX").CompareN("abdYY", 3, plString::kCaseSensitive)); + EXPECT_LT(0, plString("abcXX").CompareN("abbYY", 3, plString::kCaseSensitive)); + EXPECT_GT(0, plString("abCXX").CompareN("abcYY", 3, plString::kCaseSensitive)); + EXPECT_GT(0, plString("AbcXX").CompareN("abcYY", 3, plString::kCaseSensitive)); + + // Same length, case insensitive + EXPECT_EQ(0, plString("abcXX").CompareN("abcYY", 3, plString::kCaseInsensitive)); + EXPECT_EQ(0, plString("abcXX").CompareN("ABCYY", 3, plString::kCaseInsensitive)); + EXPECT_GT(0, plString("abcXX").CompareN("abDYY", 3, plString::kCaseInsensitive)); + EXPECT_LT(0, plString("abcXX").CompareN("abBYY", 3, plString::kCaseInsensitive)); + + // Mismatched length, case sensitive + EXPECT_LT(0, plString("abc").CompareN("ab", 3, plString::kCaseSensitive)); + EXPECT_GT(0, plString("abc").CompareN("abcd", 4, plString::kCaseSensitive)); + EXPECT_LT(0, plString("abc").CompareN("", 3, plString::kCaseSensitive)); + EXPECT_GT(0, plString("").CompareN("abc", 3, plString::kCaseSensitive)); + + // Mismatched length, case insensitive + EXPECT_LT(0, plString("abc").CompareN("Ab", 3, plString::kCaseInsensitive)); + EXPECT_GT(0, plString("abc").CompareN("Abcd", 4, plString::kCaseInsensitive)); + EXPECT_LT(0, plString("abc").CompareN("", 3, plString::kCaseInsensitive)); + EXPECT_GT(0, plString().CompareN("abc", 3, plString::kCaseInsensitive)); } -TEST(PlStringTest,ToUpper) +TEST(plString, FindChar) { - plString input = plString("abCDe"); - plString output = input.ToUpper(); - plString expected = plString("ABCDE"); - EXPECT_EQ(expected,output); + // Available char, case sensitive + EXPECT_EQ(0, plString("Aaaaaaaa").Find('A', plString::kCaseSensitive)); + EXPECT_EQ(0, plString("AaaaAaaa").Find('A', plString::kCaseSensitive)); + EXPECT_EQ(4, plString("aaaaAaaa").Find('A', plString::kCaseSensitive)); + EXPECT_EQ(7, plString("aaaaaaaA").Find('A', plString::kCaseSensitive)); + + // Available char, case insensitive + EXPECT_EQ(0, plString("Abbbbbbb").Find('A', plString::kCaseInsensitive)); + EXPECT_EQ(0, plString("AbbbAbbb").Find('A', plString::kCaseInsensitive)); + EXPECT_EQ(4, plString("bbbbAbbb").Find('A', plString::kCaseInsensitive)); + EXPECT_EQ(7, plString("bbbbbbbA").Find('A', plString::kCaseInsensitive)); + EXPECT_EQ(0, plString("abbbbbbb").Find('A', plString::kCaseInsensitive)); + EXPECT_EQ(0, plString("abbbabbb").Find('A', plString::kCaseInsensitive)); + EXPECT_EQ(4, plString("bbbbabbb").Find('A', plString::kCaseInsensitive)); + EXPECT_EQ(7, plString("bbbbbbba").Find('A', plString::kCaseInsensitive)); + EXPECT_EQ(0, plString("Abbbbbbb").Find('a', plString::kCaseInsensitive)); + EXPECT_EQ(0, plString("AbbbAbbb").Find('a', plString::kCaseInsensitive)); + EXPECT_EQ(4, plString("bbbbAbbb").Find('a', plString::kCaseInsensitive)); + EXPECT_EQ(7, plString("bbbbbbbA").Find('a', plString::kCaseInsensitive)); + + // Unavailable char + EXPECT_EQ(-1, plString("AaaaAaaa").Find('C', plString::kCaseSensitive)); + EXPECT_EQ(-1, plString("caaacaaa").Find('C', plString::kCaseSensitive)); + EXPECT_EQ(-1, plString("AaaaAaaa").Find('C', plString::kCaseInsensitive)); + + // Empty string + EXPECT_EQ(-1, plString().Find('A', plString::kCaseSensitive)); + EXPECT_EQ(-1, plString().Find('A', plString::kCaseInsensitive)); } -TEST(PlStringTest,ToLower) +TEST(plString, FindLast) { - plString input = plString("aBcDe"); - plString output = input.ToLower(); - plString expected = plString("abcde"); - EXPECT_EQ(expected,output); + // Available char, case sensitive + EXPECT_EQ(0, plString("Aaaaaaaa").FindLast('A', plString::kCaseSensitive)); + EXPECT_EQ(4, plString("AaaaAaaa").FindLast('A', plString::kCaseSensitive)); + EXPECT_EQ(4, plString("aaaaAaaa").FindLast('A', plString::kCaseSensitive)); + EXPECT_EQ(7, plString("aaaaaaaA").FindLast('A', plString::kCaseSensitive)); + + // Available char, case insensitive + EXPECT_EQ(0, plString("Abbbbbbb").FindLast('A', plString::kCaseInsensitive)); + EXPECT_EQ(4, plString("AbbbAbbb").FindLast('A', plString::kCaseInsensitive)); + EXPECT_EQ(4, plString("bbbbAbbb").FindLast('A', plString::kCaseInsensitive)); + EXPECT_EQ(7, plString("bbbbbbbA").FindLast('A', plString::kCaseInsensitive)); + EXPECT_EQ(0, plString("abbbbbbb").FindLast('A', plString::kCaseInsensitive)); + EXPECT_EQ(4, plString("abbbabbb").FindLast('A', plString::kCaseInsensitive)); + EXPECT_EQ(4, plString("bbbbabbb").FindLast('A', plString::kCaseInsensitive)); + EXPECT_EQ(7, plString("bbbbbbba").FindLast('A', plString::kCaseInsensitive)); + EXPECT_EQ(0, plString("Abbbbbbb").FindLast('a', plString::kCaseInsensitive)); + EXPECT_EQ(4, plString("AbbbAbbb").FindLast('a', plString::kCaseInsensitive)); + EXPECT_EQ(4, plString("bbbbAbbb").FindLast('a', plString::kCaseInsensitive)); + EXPECT_EQ(7, plString("bbbbbbbA").FindLast('a', plString::kCaseInsensitive)); + + // Unavailable char + EXPECT_EQ(-1, plString("AaaaAaaa").FindLast('C', plString::kCaseSensitive)); + EXPECT_EQ(-1, plString("caaacaaa").FindLast('C', plString::kCaseSensitive)); + EXPECT_EQ(-1, plString("AaaaAaaa").FindLast('C', plString::kCaseInsensitive)); + + // Empty string + EXPECT_EQ(-1, plString().FindLast('A', plString::kCaseSensitive)); + EXPECT_EQ(-1, plString().FindLast('A', plString::kCaseInsensitive)); } -TEST(PlStringTest,Tokenize) +TEST(plString, FindString) { - std::vector expected; - expected.push_back(plString("a")); - expected.push_back(plString("b")); - expected.push_back(plString("c")); - expected.push_back(plString("d")); - expected.push_back(plString("è")); + // Available string, case sensitive + EXPECT_EQ(0, plString("ABCDabcd").Find("ABCD", plString::kCaseSensitive)); + EXPECT_EQ(4, plString("abcdABCDABCDabcd").Find("ABCD", plString::kCaseSensitive)); + EXPECT_EQ(4, plString("abcdABCDabcd").Find("ABCD", plString::kCaseSensitive)); + EXPECT_EQ(4, plString("abcdABCD").Find("ABCD", plString::kCaseSensitive)); + + // Available string, case insensitive + EXPECT_EQ(0, plString("ABCDxxxx").Find("ABCD", plString::kCaseInsensitive)); + EXPECT_EQ(4, plString("xxxxABCDABCDxxxx").Find("ABCD", plString::kCaseInsensitive)); + EXPECT_EQ(4, plString("xxxxABCDxxxx").Find("ABCD", plString::kCaseInsensitive)); + EXPECT_EQ(4, plString("xxxxABCD").Find("ABCD", plString::kCaseInsensitive)); + EXPECT_EQ(0, plString("abcdxxxx").Find("ABCD", plString::kCaseInsensitive)); + EXPECT_EQ(4, plString("xxxxabcdABCDxxxx").Find("ABCD", plString::kCaseInsensitive)); + EXPECT_EQ(4, plString("xxxxabcdxxxx").Find("ABCD", plString::kCaseInsensitive)); + EXPECT_EQ(4, plString("xxxxabcd").Find("ABCD", plString::kCaseInsensitive)); + EXPECT_EQ(0, plString("ABCDxxxx").Find("abcd", plString::kCaseInsensitive)); + EXPECT_EQ(4, plString("xxxxABCDabcdxxxx").Find("abcd", plString::kCaseInsensitive)); + EXPECT_EQ(4, plString("xxxxABCDxxxx").Find("abcd", plString::kCaseInsensitive)); + EXPECT_EQ(4, plString("xxxxABCD").Find("abcd", plString::kCaseInsensitive)); + + // Unavailable string + EXPECT_EQ(-1, plString("xxxx").Find("ABCD", plString::kCaseSensitive)); + EXPECT_EQ(-1, plString("xxxx").Find("ABCD", plString::kCaseInsensitive)); + + // Empty string + EXPECT_EQ(-1, plString().Find("AAAA", plString::kCaseSensitive)); + EXPECT_EQ(-1, plString().Find("AAAA", plString::kCaseInsensitive)); + + // Unicode substring + plString haystack; + haystack = plString("xxxx") + plString::FromUtf32(test_data); + EXPECT_EQ(4, haystack.Find(utf8_test_data, plString::kCaseSensitive)); + EXPECT_EQ(4, haystack.Find(utf8_test_data, plString::kCaseInsensitive)); + + haystack = plString::FromUtf32(test_data) + plString("xxxx"); + EXPECT_EQ(0, haystack.Find(utf8_test_data, plString::kCaseSensitive)); + EXPECT_EQ(0, haystack.Find(utf8_test_data, plString::kCaseInsensitive)); + + haystack = plString("xxxx") + plString::FromUtf32(test_data) + plString("xxxx"); + EXPECT_EQ(4, haystack.Find(utf8_test_data, plString::kCaseSensitive)); + EXPECT_EQ(4, haystack.Find(utf8_test_data, plString::kCaseInsensitive)); +} - plString input = plString("a\t\tb\n;c-d;è"); - std::vector output = input.Tokenize("\t\n-;"); - EXPECT_EQ(expected,output); +//TODO: test regex functions +TEST(plString, Trim) +{ + EXPECT_EQ(plString("xxx "), plString(" xxx ").TrimLeft(" \t\r\n")); + EXPECT_EQ(plString("xxx\t"), plString("\txxx\t").TrimLeft(" \t\r\n")); + EXPECT_EQ(plString("xxx\r\n"), plString("\r\nxxx\r\n").TrimLeft(" \t\r\n")); + EXPECT_EQ(plString(" xxx "), plString(" xxx ").TrimLeft("abc")); + EXPECT_EQ(plString(" xxx "), plString(" xxx ").TrimLeft("x")); + + EXPECT_EQ(plString(" xxx"), plString(" xxx ").TrimRight(" \t\r\n")); + EXPECT_EQ(plString("\txxx"), plString("\txxx\t").TrimRight(" \t\r\n")); + EXPECT_EQ(plString("\r\nxxx"), plString("\r\nxxx\r\n").TrimRight(" \t\r\n")); + EXPECT_EQ(plString(" xxx "), plString(" xxx ").TrimRight("abc")); + EXPECT_EQ(plString(" xxx "), plString(" xxx ").TrimRight("x")); + + EXPECT_EQ(plString("xxx"), plString(" xxx ").Trim(" \t\r\n")); + EXPECT_EQ(plString("xxx"), plString("\txxx\t").Trim(" \t\r\n")); + EXPECT_EQ(plString("xxx"), plString("\r\nxxx\r\n").Trim(" \t\r\n")); + EXPECT_EQ(plString(" xxx "), plString(" xxx ").Trim("abc")); + EXPECT_EQ(plString(" xxx "), plString(" xxx ").Trim("x")); } -TEST(PlStringTest,Split) +TEST(plString, Substrings) { - std::vector expected; - expected.push_back(plString("a")); - expected.push_back(plString("b")); - expected.push_back(plString("c")); - expected.push_back(plString("d")); - expected.push_back(plString("è")); - - plString input = plString("a-b-c-d-è"); - std::vector output = input.Split("-",4); - EXPECT_EQ(expected,output); - + EXPECT_EQ(plString("AAA"), plString("AAA").Left(3)); + EXPECT_EQ(plString("AAA"), plString("AAAxxxx").Left(3)); + EXPECT_EQ(plString("A"), plString("A").Left(3)); + EXPECT_EQ(plString(""), plString("").Left(3)); + + EXPECT_EQ(plString("AAA"), plString("AAA").Right(3)); + EXPECT_EQ(plString("AAA"), plString("xxxxAAA").Right(3)); + EXPECT_EQ(plString("A"), plString("A").Right(3)); + EXPECT_EQ(plString(""), plString("").Right(3)); + + EXPECT_EQ(plString("AAA"), plString("AAAxxxx").Substr(0, 3)); + EXPECT_EQ(plString("AAA"), plString("xxxxAAA").Substr(4, 3)); + EXPECT_EQ(plString("AAA"), plString("xxAAAxx").Substr(2, 3)); + + EXPECT_EQ(plString(""), plString("AAAA").Substr(2, 0)); + EXPECT_EQ(plString("AA"), plString("AAAA").Substr(2, 4)); + EXPECT_EQ(plString(""), plString("AAAA").Substr(6, 4)); + EXPECT_EQ(plString("AAAA"), plString("AAAA").Substr(0, 4)); + EXPECT_EQ(plString(""), plString("").Substr(0, 4)); + + // Negative indexes start from the right + EXPECT_EQ(plString("AAA"), plString("xxxxAAA").Substr(-3, 3)); + EXPECT_EQ(plString("AAA"), plString("xxAAAxx").Substr(-5, 3)); + EXPECT_EQ(plString("AAA"), plString("xxxxAAA").Substr(-3, 6)); + EXPECT_EQ(plString("AAA"), plString("AAAxxxx").Substr(-10, 3)); + EXPECT_EQ(plString("AAA"), plString("AAA").Substr(-10, 10)); } -TEST(PlStringTest,Fill) +TEST(plString, Replace) { - plString expected = plString("aaaaa"); - plString output = plString::Fill(5,'a'); - EXPECT_EQ(expected,output); + EXPECT_EQ(plString("xxYYxx"), plString("xxAAxx").Replace("A", "Y")); + EXPECT_EQ(plString("xxAAxx"), plString("xxAAxx").Replace("XX", "Y")); + EXPECT_EQ(plString("xxxx"), plString("xxAAxx").Replace("A", "")); + EXPECT_EQ(plString("xxREPLACExx"), plString("xxFINDxx").Replace("FIND", "REPLACE")); + EXPECT_EQ(plString("xxREPLACExxREPLACExx"), plString("xxFINDxxFINDxx").Replace("FIND", "REPLACE")); + + EXPECT_EQ(plString("YYxx"), plString("AAxx").Replace("A", "Y")); + EXPECT_EQ(plString("AAxx"), plString("AAxx").Replace("XX", "Y")); + EXPECT_EQ(plString("xx"), plString("AAxx").Replace("A", "")); + EXPECT_EQ(plString("REPLACExx"), plString("FINDxx").Replace("FIND", "REPLACE")); + EXPECT_EQ(plString("REPLACExxREPLACExx"), plString("FINDxxFINDxx").Replace("FIND", "REPLACE")); + + EXPECT_EQ(plString("xxYY"), plString("xxAA").Replace("A", "Y")); + EXPECT_EQ(plString("xxAA"), plString("xxAA").Replace("XX", "Y")); + EXPECT_EQ(plString("xx"), plString("xxAA").Replace("A", "")); + EXPECT_EQ(plString("xxREPLACE"), plString("xxFIND").Replace("FIND", "REPLACE")); + EXPECT_EQ(plString("xxREPLACExxREPLACE"), plString("xxFINDxxFIND").Replace("FIND", "REPLACE")); + + EXPECT_EQ(plString("YY"), plString("AA").Replace("A", "Y")); + EXPECT_EQ(plString("AA"), plString("AA").Replace("XX", "Y")); + EXPECT_EQ(plString(""), plString("AA").Replace("A", "")); + EXPECT_EQ(plString("REPLACE"), plString("FIND").Replace("FIND", "REPLACE")); + EXPECT_EQ(plString("REPLACExxREPLACE"), plString("FINDxxFIND").Replace("FIND", "REPLACE")); } -//overload operator+ -TEST(PlStringTest,Addition) +TEST(plString, CaseConvert) { - plString expected = "abcde"; - plString input1 = "ab"; - plString input2 = "cde"; + /* Edge cases: + * '@' = 'A' - 1 + * '[' = 'Z' + 1 + * '`' = 'a' - 1 + * '{' = 'z' + 1 + */ + + EXPECT_EQ(plString("AAZZ"), plString("aazz").ToUpper()); + EXPECT_EQ(plString("AAZZ"), plString("AAZZ").ToUpper()); + EXPECT_EQ(plString("@AZ[`AZ{"), plString("@AZ[`az{").ToUpper()); + EXPECT_EQ(plString(""), plString("").ToUpper()); + + EXPECT_EQ(plString("aazz"), plString("aazz").ToLower()); + EXPECT_EQ(plString("aazz"), plString("AAZZ").ToLower()); + EXPECT_EQ(plString("@az[`az{"), plString("@AZ[`az{").ToLower()); + EXPECT_EQ(plString(""), plString("").ToLower()); +} - //plstring+plstring - plString output = input1+input2; - EXPECT_EQ(expected,output); +TEST(plString, Tokenize) +{ + std::vector expected1; + expected1.push_back("aaa"); + expected1.push_back("b"); + expected1.push_back("ccc"); + expected1.push_back("d"); + expected1.push_back("èèè"); + const plString input1("aaa\t\tb\n;ccc-d;èèè"); + EXPECT_EQ(expected1, input1.Tokenize("\t\n-;")); + + std::vector expected2; + expected2.push_back("aaa\t\tb\n"); + expected2.push_back("ccc-d"); + expected2.push_back("èèè"); + EXPECT_EQ(expected2, input1.Tokenize(";")); + + std::vector expected3; + expected3.push_back(input1); + EXPECT_EQ(expected3, input1.Tokenize("x")); + + const plString input2("\t;aaa\t\tb\n;ccc-d;èèè--"); + EXPECT_EQ(expected1, input2.Tokenize("\t\n-;")); + + // Tokenize will return an empty vector if there are no tokens in the input + EXPECT_EQ(std::vector{}, plString("\t;\n;").Tokenize("\t\n-;")); + EXPECT_EQ(std::vector{}, plString("").Tokenize("\t\n-;")); +} - //plstring+char* - plString output1 = input1 + input2.c_str(); - EXPECT_EQ(expected,output1); +TEST(plString, Split) +{ + std::vector expected1; + expected1.push_back("aaa"); + expected1.push_back("b"); + expected1.push_back("ccc"); + expected1.push_back("d"); + expected1.push_back("èèè"); + const plString input1("aaa-b-ccc-d-èèè"); + EXPECT_EQ(expected1, input1.Split("-")); + EXPECT_EQ(expected1, input1.Split("-", 4)); + EXPECT_EQ(expected1, input1.Split("-", 10)); + + const plString input2("aaa#SEP#b#SEP#ccc#SEP#d#SEP#èèè"); + EXPECT_EQ(expected1, input2.Split("#SEP#")); + EXPECT_EQ(expected1, input2.Split("#SEP#", 4)); + EXPECT_EQ(expected1, input2.Split("#SEP#", 10)); + + std::vector expected2; + expected2.push_back("aaa"); + expected2.push_back("b"); + expected2.push_back("ccc-d-èèè"); + EXPECT_EQ(expected2, input1.Split("-", 2)); + + std::vector expected3; + expected3.push_back(input1); + EXPECT_EQ(expected3, input1.Split("-", 0)); + EXPECT_EQ(expected3, input1.Split("x")); + EXPECT_EQ(expected3, input1.Split("x", 4)); + + std::vector expected4; + expected4.push_back(""); + expected4.push_back("aaa"); + expected4.push_back("b"); + expected4.push_back("ccc"); + expected4.push_back("d"); + expected4.push_back("èèè"); + expected4.push_back(""); + const plString input3("-aaa-b-ccc-d-èèè-"); + EXPECT_EQ(expected4, input3.Split("-")); + EXPECT_EQ(expected4, input3.Split("-", 6)); + EXPECT_EQ(expected4, input3.Split("-", 10)); + + std::vector expected5; + expected5.push_back(""); + expected5.push_back(""); + expected5.push_back(""); + expected5.push_back(""); + expected5.push_back(""); + const plString input4("----"); + EXPECT_EQ(expected5, input4.Split("-")); + EXPECT_EQ(expected5, input4.Split("-", 4)); + EXPECT_EQ(expected5, input4.Split("-", 10)); + + std::vector expected6; + expected6.push_back(""); + expected6.push_back(""); + expected6.push_back("--"); + EXPECT_EQ(expected6, input4.Split("-", 2)); + + std::vector expected7; + expected7.push_back(""); + expected7.push_back(""); + expected7.push_back(""); + EXPECT_EQ(expected7, input4.Split("--")); + + std::vector expected8; + expected8.push_back(""); + expected8.push_back("--"); + EXPECT_EQ(expected8, input4.Split("--", 1)); + + // Split never provides an empty vector, even for empty input + std::vector expected9; + expected9.push_back(plString::Null); + EXPECT_EQ(expected9, plString("").Split("-")); + EXPECT_EQ(expected9, plString("").Split("-", 4)); +} - //char*+plstring - plString output2 = input1.c_str() + input2; - EXPECT_EQ(expected,output2); +TEST(plString, Fill) +{ + EXPECT_EQ(plString(""), plString::Fill(0, 'a')); + EXPECT_EQ(plString("aaaaa"), plString::Fill(5, 'a')); + EXPECT_EQ(plString("aaaaaaaaaaaaaaaaaaaa"), plString::Fill(20, 'a')); }