From d8e6d79fbd78d79dda3fcbd6fd39f5eab7e43df9 Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Fri, 13 Feb 2015 00:07:45 -0800 Subject: [PATCH 01/18] More fully test encoding conversions. Also fixes some existing bugs: - UTF-16 surrogate pairs were getting encoded incorrectly - Signed shift converting from ISO-8859-1 got too many bits - Edge case incorrect for converting to ISO-8859-1 --- Sources/Plasma/CoreLib/plString.cpp | 9 +- Sources/Tests/CoreTests/test_plString.cpp | 121 +++++++++++++++++++--- 2 files changed, 110 insertions(+), 20 deletions(-) diff --git a/Sources/Plasma/CoreLib/plString.cpp b/Sources/Plasma/CoreLib/plString.cpp index 08bab171..754b7882 100644 --- a/Sources/Plasma/CoreLib/plString.cpp +++ b/Sources/Plasma/CoreLib/plString.cpp @@ -299,9 +299,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 +347,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 +425,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/test_plString.cpp b/Sources/Tests/CoreTests/test_plString.cpp index f9cf0566..ff1fa312 100644 --- a/Sources/Tests/CoreTests/test_plString.cpp +++ b/Sources/Tests/CoreTests/test_plString.cpp @@ -4,30 +4,119 @@ #include #include +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"; + +/* 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) +{ + for ( ;; ) { + if (*left != *right) + return *left - *right; + if (*left == 0) + return (*right == 0) ? 0 : -1; + if (*right == 0) + return 1; + + ++left; + ++right; + } +} + +TEST(PlStringTest, TestHelpers) +{ + /* 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")); +} + +TEST(PlStringTest, ConvertUtf8) +{ + // From UTF-8 to plString + plString from_utf8 = plString::FromUtf8(utf8_test_data); + EXPECT_STREQ(utf8_test_data, from_utf8.c_str()); + 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()); +} -TEST(PlStringTest,ToUtf16) +TEST(PlStringTest, ConvertUtf16) { - 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 + // From UTF-16 to plString + plString from_utf16 = plString::FromUtf16(utf16_test_data); + 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())); } -TEST(PlStringTest,ToWchar) +TEST(PlStringTest, ConvertIso8859_1) { - 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()); + // From ISO-8859-1 to plString + const char latin1[] = "\x20\x7e\xa0\xff"; + const plUniChar unicode_cp0[] = { 0x20, 0x7e, 0xa0, 0xff, 0 }; + plString from_latin1 = plString::FromIso8859_1(latin1); + 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()); } -TEST(PlStringTest,ToIso8859_1) +TEST(PlStringTest, ConvertWchar) { - char text[] ="abcde"; - plStringBuffer expected = plStringBuffer(text,arrsize(text)); - plStringBuffer output = plString("abcde").ToIso8859_1(); - EXPECT_STREQ(expected.GetData(),output.GetData()); + // 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 }; + plString from_wchar = plString::FromWchar(wtext); + 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()); } TEST(PlStringTest,FindChar) From 5e39c31345a902156cbc18cbf2e4a77ba5056b78 Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Fri, 13 Feb 2015 00:13:43 -0800 Subject: [PATCH 02/18] Add test for conversion of invalid characters to a replacement --- Sources/Tests/CoreTests/test_plString.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Sources/Tests/CoreTests/test_plString.cpp b/Sources/Tests/CoreTests/test_plString.cpp index ff1fa312..efc1103d 100644 --- a/Sources/Tests/CoreTests/test_plString.cpp +++ b/Sources/Tests/CoreTests/test_plString.cpp @@ -119,6 +119,25 @@ TEST(PlStringTest, ConvertWchar) EXPECT_STREQ(wtext, to_wchar.GetData()); } +TEST(PlStringTest, ConvertInvalid) +{ + // The following should encode replacement characters for invalid chars + const plUniChar unicode_replacement[] = { 0xfffd, 0 }; + + const plUniChar char_too_big[] = { 0xffffff, 0 }; + plUnicodeBuffer too_big = plString::FromUtf32(char_too_big).GetUnicodeArray(); + EXPECT_EQ(0, T_strcmp(unicode_replacement, too_big.GetData())); + + // TODO: Invalid surrogate pairs can encode to 0xfffd, but it's handled + // by an assert right now. + + // ISO-8859-1 doesn't have \ufffd, so it uses '?' instead + const plUniChar high_char[] = { 0x1ff, 0 }; + const char latin1_replacement[] = "?"; + plStringBuffer non_latin1 = plString::FromUtf32(high_char).ToIso8859_1(); + EXPECT_STREQ(latin1_replacement, non_latin1.GetData()); +} + TEST(PlStringTest,FindChar) { plString input = plString("abCdcBAeab"); From 56553c82712738a5046bbce81b94c6ac386e8eac Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Fri, 13 Feb 2015 18:36:09 -0800 Subject: [PATCH 03/18] Use U+FFFD on Debug code too, and finish test cases for replacement --- Sources/Plasma/CoreLib/plString.cpp | 24 +++++++++++++-------- Sources/Tests/CoreTests/test_plString.cpp | 26 +++++++++++++++++------ 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/Sources/Plasma/CoreLib/plString.cpp b/Sources/Plasma/CoreLib/plString.cpp index 754b7882..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; diff --git a/Sources/Tests/CoreTests/test_plString.cpp b/Sources/Tests/CoreTests/test_plString.cpp index efc1103d..9e3988b1 100644 --- a/Sources/Tests/CoreTests/test_plString.cpp +++ b/Sources/Tests/CoreTests/test_plString.cpp @@ -123,18 +123,30 @@ TEST(PlStringTest, ConvertInvalid) { // The following should encode replacement characters for invalid chars const plUniChar unicode_replacement[] = { 0xfffd, 0 }; + const char latin1_replacement[] = "?"; - const plUniChar char_too_big[] = { 0xffffff, 0 }; - plUnicodeBuffer too_big = plString::FromUtf32(char_too_big).GetUnicodeArray(); + // 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())); - // TODO: Invalid surrogate pairs can encode to 0xfffd, but it's handled - // by an assert right now. + // 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 high_char[] = { 0x1ff, 0 }; - const char latin1_replacement[] = "?"; - plStringBuffer non_latin1 = plString::FromUtf32(high_char).ToIso8859_1(); + const plUniChar non_latin1_c[] = { 0x1ff, 0 }; + plStringBuffer non_latin1 = plString::FromUtf32(non_latin1_c).ToIso8859_1(); EXPECT_STREQ(latin1_replacement, non_latin1.GetData()); } From bde485109c2ec22ed007a9a39edc7fc662fd3366 Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Sun, 7 Jun 2015 22:32:27 -0700 Subject: [PATCH 04/18] More extensive FindChar --- Sources/Tests/CoreTests/test_plString.cpp | 176 ++++++++++++++++------ 1 file changed, 130 insertions(+), 46 deletions(-) diff --git a/Sources/Tests/CoreTests/test_plString.cpp b/Sources/Tests/CoreTests/test_plString.cpp index 9e3988b1..93f20d99 100644 --- a/Sources/Tests/CoreTests/test_plString.cpp +++ b/Sources/Tests/CoreTests/test_plString.cpp @@ -150,66 +150,150 @@ TEST(PlStringTest, ConvertInvalid) EXPECT_STREQ(latin1_replacement, non_latin1.GetData()); } -TEST(PlStringTest,FindChar) +TEST(PlStringTest, FindChar) { - plString input = plString("abCdcBAeab"); - int result=0; - //available char, case sensitive - result = input.Find('B',plString::kCaseSensitive); - EXPECT_EQ(5,result); + int result; - //available char, case insensitive - result = input.Find('B',plString::kCaseInsensitive); - EXPECT_EQ(1,result); + // Available char, case sensitive + result = plString("Aaaaaaaa").Find('A', plString::kCaseSensitive); + EXPECT_EQ(0, result); - //unavailable char, case sensitive - result = input.Find('f',plString::kCaseSensitive); - EXPECT_EQ(-1,result); + result = plString("AaaaAaaa").Find('A', plString::kCaseSensitive); + EXPECT_EQ(0, result); - //unavailable char, case insensitive - result=0; - result = input.Find('f',plString::kCaseInsensitive); - EXPECT_EQ(-1,result); + result = plString("aaaaAaaa").Find('A', plString::kCaseSensitive); + EXPECT_EQ(4, result); - plString input1 = plString("abCdcBÁèab"); - //available accented char, case sensitive - result = input1.Find('Á',plString::kCaseSensitive); - EXPECT_EQ(7,result); + result = plString("aaaaaaaA").Find('A', plString::kCaseSensitive); + EXPECT_EQ(7, result); - //available accented char, case insensitive - result = input1.Find('è',plString::kCaseInsensitive); - EXPECT_EQ(9,result); + // Available char, case insensitive + result = plString("Abbbbbbb").Find('A', plString::kCaseInsensitive); + EXPECT_EQ(0, result); + + result = plString("AbbbAbbb").Find('A', plString::kCaseInsensitive); + EXPECT_EQ(0, result); + + result = plString("bbbbAbbb").Find('A', plString::kCaseInsensitive); + EXPECT_EQ(4, result); + + result = plString("bbbbbbbA").Find('A', plString::kCaseInsensitive); + EXPECT_EQ(7, result); + + result = plString("abbbbbbb").Find('A', plString::kCaseInsensitive); + EXPECT_EQ(0, result); + + result = plString("abbbabbb").Find('A', plString::kCaseInsensitive); + EXPECT_EQ(0, result); + + result = plString("bbbbabbb").Find('A', plString::kCaseInsensitive); + EXPECT_EQ(4, result); + + result = plString("bbbbbbba").Find('A', plString::kCaseInsensitive); + EXPECT_EQ(7, result); + + result = plString("Abbbbbbb").Find('a', plString::kCaseInsensitive); + EXPECT_EQ(0, result); + + result = plString("AbbbAbbb").Find('a', plString::kCaseInsensitive); + EXPECT_EQ(0, result); + + result = plString("bbbbAbbb").Find('a', plString::kCaseInsensitive); + EXPECT_EQ(4, result); + + result = plString("bbbbbbbA").Find('a', plString::kCaseInsensitive); + EXPECT_EQ(7, result); + + // Unavailable char, case sensitive + result = plString("AaaaAaaa").Find('C', plString::kCaseSensitive); + EXPECT_EQ(-1, result); + + result = plString("caaacaaa").Find('C', plString::kCaseSensitive); + EXPECT_EQ(-1, result); + + // Unavailable char, case insensitive + result = plString("AaaaAaaa").Find('C', plString::kCaseInsensitive); + EXPECT_EQ(-1, result); + + // Empty string + result = plString().Find('A', plString::kCaseSensitive); + EXPECT_EQ(-1, result); + + result = plString().Find('A', plString::kCaseInsensitive); + EXPECT_EQ(-1, result); } -TEST(PlStringTest,FindLast) +TEST(PlStringTest, FindLast) { - plString input = plString("abCdcBAeab"); - int result=0; - //available char, case sensitive - result = input.FindLast('B',plString::kCaseSensitive); - EXPECT_EQ(5,result); + int result; - //available char, case insensitive - result = input.FindLast('B',plString::kCaseInsensitive); - EXPECT_EQ(9,result); + // Available char, case sensitive + result = plString("Aaaaaaaa").FindLast('A', plString::kCaseSensitive); + EXPECT_EQ(0, result); - //unavailable char, case sensitive - result = input.FindLast('f',plString::kCaseSensitive); - EXPECT_EQ(-1,result); + result = plString("AaaaAaaa").FindLast('A', plString::kCaseSensitive); + EXPECT_EQ(4, result); - //unavailable char, case insensitive - result=0; - result = input.FindLast('f',plString::kCaseInsensitive); - EXPECT_EQ(-1,result); + result = plString("aaaaAaaa").FindLast('A', plString::kCaseSensitive); + EXPECT_EQ(4, result); - plString input1 = plString("éeÉß"); - //available accented char, case sensitive - result = input1.FindLast('e',plString::kCaseSensitive); - EXPECT_EQ(2,result); + result = plString("aaaaaaaA").FindLast('A', plString::kCaseSensitive); + EXPECT_EQ(7, result); + + // Available char, case insensitive + result = plString("Abbbbbbb").FindLast('A', plString::kCaseInsensitive); + EXPECT_EQ(0, result); + + result = plString("AbbbAbbb").FindLast('A', plString::kCaseInsensitive); + EXPECT_EQ(4, result); + + result = plString("bbbbAbbb").FindLast('A', plString::kCaseInsensitive); + EXPECT_EQ(4, result); + + result = plString("bbbbbbbA").FindLast('A', plString::kCaseInsensitive); + EXPECT_EQ(7, result); + + result = plString("abbbbbbb").FindLast('A', plString::kCaseInsensitive); + EXPECT_EQ(0, result); + + result = plString("abbbabbb").FindLast('A', plString::kCaseInsensitive); + EXPECT_EQ(4, result); + + result = plString("bbbbabbb").FindLast('A', plString::kCaseInsensitive); + EXPECT_EQ(4, result); + + result = plString("bbbbbbba").FindLast('A', plString::kCaseInsensitive); + EXPECT_EQ(7, result); + + result = plString("Abbbbbbb").FindLast('a', plString::kCaseInsensitive); + EXPECT_EQ(0, result); + + result = plString("AbbbAbbb").FindLast('a', plString::kCaseInsensitive); + EXPECT_EQ(4, result); + + result = plString("bbbbAbbb").FindLast('a', plString::kCaseInsensitive); + EXPECT_EQ(4, result); + + result = plString("bbbbbbbA").FindLast('a', plString::kCaseInsensitive); + EXPECT_EQ(7, result); + + // Unavailable char, case sensitive + result = plString("AaaaAaaa").FindLast('C', plString::kCaseSensitive); + EXPECT_EQ(-1, result); + + result = plString("caaacaaa").FindLast('C', plString::kCaseSensitive); + EXPECT_EQ(-1, result); + + // Unavailable char, case insensitive + result = plString("AaaaAaaa").FindLast('C', plString::kCaseInsensitive); + EXPECT_EQ(-1, result); + + // Empty string + result = plString().FindLast('A', plString::kCaseSensitive); + EXPECT_EQ(-1, result); - //available accented char, case insensitive - result = input1.FindLast('ß',plString::kCaseInsensitive); - EXPECT_EQ(6,result); + result = plString().FindLast('A', plString::kCaseInsensitive); + EXPECT_EQ(-1, result); } TEST(PlStringTest,FindString) From d5e6abdc5b6b97cea4c784100eeaad02a304d72c Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Sun, 7 Jun 2015 22:47:08 -0700 Subject: [PATCH 05/18] Also test conversion length and empty string conversions --- Sources/Tests/CoreTests/test_plString.cpp | 36 +++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/Sources/Tests/CoreTests/test_plString.cpp b/Sources/Tests/CoreTests/test_plString.cpp index 93f20d99..7a5a2fac 100644 --- a/Sources/Tests/CoreTests/test_plString.cpp +++ b/Sources/Tests/CoreTests/test_plString.cpp @@ -22,6 +22,7 @@ static const char utf8_test_data[] = "\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[] = { @@ -62,6 +63,9 @@ TEST(PlStringTest, TestHelpers) 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, ConvertUtf8) @@ -69,24 +73,42 @@ TEST(PlStringTest, ConvertUtf8) // 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, ConvertUtf16) { // 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(), "")); } TEST(PlStringTest, ConvertIso8859_1) @@ -94,13 +116,20 @@ TEST(PlStringTest, ConvertIso8859_1) // 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, ConvertWchar) @@ -110,13 +139,20 @@ TEST(PlStringTest, ConvertWchar) 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, ConvertInvalid) From d7dec159cf7e1dc1f2640ded1696d818af0977e9 Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Mon, 8 Jun 2015 01:01:45 -0700 Subject: [PATCH 06/18] Also extend FindString, and get rid of unnecessary temporary --- Sources/Tests/CoreTests/test_plString.cpp | 292 +++++++++------------- 1 file changed, 116 insertions(+), 176 deletions(-) diff --git a/Sources/Tests/CoreTests/test_plString.cpp b/Sources/Tests/CoreTests/test_plString.cpp index 7a5a2fac..c226f99f 100644 --- a/Sources/Tests/CoreTests/test_plString.cpp +++ b/Sources/Tests/CoreTests/test_plString.cpp @@ -186,182 +186,141 @@ TEST(PlStringTest, ConvertInvalid) EXPECT_STREQ(latin1_replacement, non_latin1.GetData()); } -TEST(PlStringTest, FindChar) +TEST(PlStringTest, Concatenation) { - int result; + // 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, FindChar) +{ // Available char, case sensitive - result = plString("Aaaaaaaa").Find('A', plString::kCaseSensitive); - EXPECT_EQ(0, result); - - result = plString("AaaaAaaa").Find('A', plString::kCaseSensitive); - EXPECT_EQ(0, result); - - result = plString("aaaaAaaa").Find('A', plString::kCaseSensitive); - EXPECT_EQ(4, result); - - result = plString("aaaaaaaA").Find('A', plString::kCaseSensitive); - EXPECT_EQ(7, result); + 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 - result = plString("Abbbbbbb").Find('A', plString::kCaseInsensitive); - EXPECT_EQ(0, result); - - result = plString("AbbbAbbb").Find('A', plString::kCaseInsensitive); - EXPECT_EQ(0, result); - - result = plString("bbbbAbbb").Find('A', plString::kCaseInsensitive); - EXPECT_EQ(4, result); - - result = plString("bbbbbbbA").Find('A', plString::kCaseInsensitive); - EXPECT_EQ(7, result); - - result = plString("abbbbbbb").Find('A', plString::kCaseInsensitive); - EXPECT_EQ(0, result); - - result = plString("abbbabbb").Find('A', plString::kCaseInsensitive); - EXPECT_EQ(0, result); - - result = plString("bbbbabbb").Find('A', plString::kCaseInsensitive); - EXPECT_EQ(4, result); - - result = plString("bbbbbbba").Find('A', plString::kCaseInsensitive); - EXPECT_EQ(7, result); - - result = plString("Abbbbbbb").Find('a', plString::kCaseInsensitive); - EXPECT_EQ(0, result); - - result = plString("AbbbAbbb").Find('a', plString::kCaseInsensitive); - EXPECT_EQ(0, result); - - result = plString("bbbbAbbb").Find('a', plString::kCaseInsensitive); - EXPECT_EQ(4, result); - - result = plString("bbbbbbbA").Find('a', plString::kCaseInsensitive); - EXPECT_EQ(7, result); - - // Unavailable char, case sensitive - result = plString("AaaaAaaa").Find('C', plString::kCaseSensitive); - EXPECT_EQ(-1, result); - - result = plString("caaacaaa").Find('C', plString::kCaseSensitive); - EXPECT_EQ(-1, result); - - // Unavailable char, case insensitive - result = plString("AaaaAaaa").Find('C', plString::kCaseInsensitive); - EXPECT_EQ(-1, result); + 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 - result = plString().Find('A', plString::kCaseSensitive); - EXPECT_EQ(-1, result); - - result = plString().Find('A', plString::kCaseInsensitive); - EXPECT_EQ(-1, result); + EXPECT_EQ(-1, plString().Find('A', plString::kCaseSensitive)); + EXPECT_EQ(-1, plString().Find('A', plString::kCaseInsensitive)); } TEST(PlStringTest, FindLast) { - int result; - // Available char, case sensitive - result = plString("Aaaaaaaa").FindLast('A', plString::kCaseSensitive); - EXPECT_EQ(0, result); - - result = plString("AaaaAaaa").FindLast('A', plString::kCaseSensitive); - EXPECT_EQ(4, result); - - result = plString("aaaaAaaa").FindLast('A', plString::kCaseSensitive); - EXPECT_EQ(4, result); - - result = plString("aaaaaaaA").FindLast('A', plString::kCaseSensitive); - EXPECT_EQ(7, result); + 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 - result = plString("Abbbbbbb").FindLast('A', plString::kCaseInsensitive); - EXPECT_EQ(0, result); - - result = plString("AbbbAbbb").FindLast('A', plString::kCaseInsensitive); - EXPECT_EQ(4, result); - - result = plString("bbbbAbbb").FindLast('A', plString::kCaseInsensitive); - EXPECT_EQ(4, result); - - result = plString("bbbbbbbA").FindLast('A', plString::kCaseInsensitive); - EXPECT_EQ(7, result); - - result = plString("abbbbbbb").FindLast('A', plString::kCaseInsensitive); - EXPECT_EQ(0, result); - - result = plString("abbbabbb").FindLast('A', plString::kCaseInsensitive); - EXPECT_EQ(4, result); - - result = plString("bbbbabbb").FindLast('A', plString::kCaseInsensitive); - EXPECT_EQ(4, result); - - result = plString("bbbbbbba").FindLast('A', plString::kCaseInsensitive); - EXPECT_EQ(7, result); - - result = plString("Abbbbbbb").FindLast('a', plString::kCaseInsensitive); - EXPECT_EQ(0, result); - - result = plString("AbbbAbbb").FindLast('a', plString::kCaseInsensitive); - EXPECT_EQ(4, result); - - result = plString("bbbbAbbb").FindLast('a', plString::kCaseInsensitive); - EXPECT_EQ(4, result); - - result = plString("bbbbbbbA").FindLast('a', plString::kCaseInsensitive); - EXPECT_EQ(7, result); - - // Unavailable char, case sensitive - result = plString("AaaaAaaa").FindLast('C', plString::kCaseSensitive); - EXPECT_EQ(-1, result); - - result = plString("caaacaaa").FindLast('C', plString::kCaseSensitive); - EXPECT_EQ(-1, result); - - // Unavailable char, case insensitive - result = plString("AaaaAaaa").FindLast('C', plString::kCaseInsensitive); - EXPECT_EQ(-1, result); + 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 - result = plString().FindLast('A', plString::kCaseSensitive); - EXPECT_EQ(-1, result); - - result = plString().FindLast('A', plString::kCaseInsensitive); - EXPECT_EQ(-1, result); + EXPECT_EQ(-1, plString().FindLast('A', plString::kCaseSensitive)); + EXPECT_EQ(-1, plString().FindLast('A', plString::kCaseInsensitive)); } -TEST(PlStringTest,FindString) +TEST(PlStringTest, FindString) { - 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); + // 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)); } //TODO: test regex functions @@ -497,22 +456,3 @@ TEST(PlStringTest,Fill) EXPECT_EQ(expected,output); } -//overload operator+ -TEST(PlStringTest,Addition) -{ - plString expected = "abcde"; - plString input1 = "ab"; - plString input2 = "cde"; - - //plstring+plstring - plString output = input1+input2; - EXPECT_EQ(expected,output); - - //plstring+char* - plString output1 = input1 + input2.c_str(); - EXPECT_EQ(expected,output1); - - //char*+plstring - plString output2 = input1.c_str() + input2; - EXPECT_EQ(expected,output2); -} From 185066ac52e1125d78a3ae566311d79cb525bb1d Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Mon, 8 Jun 2015 02:07:14 -0700 Subject: [PATCH 07/18] Add comparison tests --- Sources/Tests/CoreTests/test_plString.cpp | 58 +++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/Sources/Tests/CoreTests/test_plString.cpp b/Sources/Tests/CoreTests/test_plString.cpp index c226f99f..b1d90a03 100644 --- a/Sources/Tests/CoreTests/test_plString.cpp +++ b/Sources/Tests/CoreTests/test_plString.cpp @@ -214,6 +214,64 @@ TEST(PlStringTest, Concatenation) EXPECT_EQ(input1, "" + input1); } +TEST(PlStringTest, Compare) +{ + // 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(PlStringTest, 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, FindChar) { // Available char, case sensitive From f626b9ef3d6e855c0a86905f191cdef2e344648d Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Fri, 3 Jul 2015 23:20:32 -0700 Subject: [PATCH 08/18] More trim test cases --- Sources/Tests/CoreTests/test_plString.cpp | 51 ++++++++--------------- 1 file changed, 18 insertions(+), 33 deletions(-) diff --git a/Sources/Tests/CoreTests/test_plString.cpp b/Sources/Tests/CoreTests/test_plString.cpp index b1d90a03..90a8cdb6 100644 --- a/Sources/Tests/CoreTests/test_plString.cpp +++ b/Sources/Tests/CoreTests/test_plString.cpp @@ -383,40 +383,25 @@ TEST(PlStringTest, FindString) //TODO: test regex functions -TEST(PlStringTest,TrimLeft) +TEST(PlStringTest, Trim) { - 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); -} - -TEST(PlStringTest,TrimRight) -{ - 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); -} - -TEST(PlStringTest,Trim) -{ - 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); + 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,Substr) From e65b53956fb17dab882c2087e2ba948abc14396f Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Sun, 5 Jul 2015 10:45:24 -0700 Subject: [PATCH 09/18] More extensive Left/Right/Substr, and use 'plString' for the test group --- Sources/Tests/CoreTests/test_plString.cpp | 78 ++++++++++++----------- 1 file changed, 40 insertions(+), 38 deletions(-) diff --git a/Sources/Tests/CoreTests/test_plString.cpp b/Sources/Tests/CoreTests/test_plString.cpp index 90a8cdb6..d6c88335 100644 --- a/Sources/Tests/CoreTests/test_plString.cpp +++ b/Sources/Tests/CoreTests/test_plString.cpp @@ -55,7 +55,7 @@ static int T_strcmp(const _Ch *left, const _Ch *right) } } -TEST(PlStringTest, TestHelpers) +TEST(plString, TestHelpers) { /* Ensure the utilities for testing the module function properly */ EXPECT_EQ(0, T_strcmp("abc", "abc")); @@ -68,7 +68,7 @@ TEST(PlStringTest, TestHelpers) EXPECT_LT(0, T_strcmp("a", "")); } -TEST(PlStringTest, ConvertUtf8) +TEST(plString, ConvertUtf8) { // From UTF-8 to plString plString from_utf8 = plString::FromUtf8(utf8_test_data); @@ -92,7 +92,7 @@ TEST(PlStringTest, ConvertUtf8) EXPECT_EQ(0, T_strcmp(empty.c_str(), "")); } -TEST(PlStringTest, ConvertUtf16) +TEST(plString, ConvertUtf16) { // From UTF-16 to plString plString from_utf16 = plString::FromUtf16(utf16_test_data); @@ -111,7 +111,7 @@ TEST(PlStringTest, ConvertUtf16) EXPECT_EQ(0, T_strcmp(empty.c_str(), "")); } -TEST(PlStringTest, ConvertIso8859_1) +TEST(plString, ConvertIso8859_1) { // From ISO-8859-1 to plString const char latin1[] = "\x20\x7e\xa0\xff"; @@ -132,7 +132,7 @@ TEST(PlStringTest, ConvertIso8859_1) EXPECT_EQ(0, T_strcmp(empty.c_str(), "")); } -TEST(PlStringTest, ConvertWchar) +TEST(plString, ConvertWchar) { // UTF-8 and UTF-16 are already tested, so just make sure we test // wchar_t and L"" conversions @@ -155,7 +155,7 @@ TEST(PlStringTest, ConvertWchar) EXPECT_EQ(0, T_strcmp(empty.c_str(), "")); } -TEST(PlStringTest, ConvertInvalid) +TEST(plString, ConvertInvalid) { // The following should encode replacement characters for invalid chars const plUniChar unicode_replacement[] = { 0xfffd, 0 }; @@ -186,7 +186,7 @@ TEST(PlStringTest, ConvertInvalid) EXPECT_STREQ(latin1_replacement, non_latin1.GetData()); } -TEST(PlStringTest, Concatenation) +TEST(plString, Concatenation) { // If this changes, this test may need to be updated to match ASSERT_EQ(16, SSO_CHARS); @@ -214,7 +214,7 @@ TEST(PlStringTest, Concatenation) EXPECT_EQ(input1, "" + input1); } -TEST(PlStringTest, Compare) +TEST(plString, Compare) { // Same length, case sensitive EXPECT_EQ(0, plString("abc").Compare("abc", plString::kCaseSensitive)); @@ -244,7 +244,7 @@ TEST(PlStringTest, Compare) EXPECT_GT(0, plString().Compare("abc", plString::kCaseInsensitive)); } -TEST(PlStringTest, CompareN) +TEST(plString, CompareN) { // Same length, case sensitive EXPECT_EQ(0, plString("abcXX").CompareN("abcYY", 3, plString::kCaseSensitive)); @@ -272,7 +272,7 @@ TEST(PlStringTest, CompareN) EXPECT_GT(0, plString().CompareN("abc", 3, plString::kCaseInsensitive)); } -TEST(PlStringTest, FindChar) +TEST(plString, FindChar) { // Available char, case sensitive EXPECT_EQ(0, plString("Aaaaaaaa").Find('A', plString::kCaseSensitive)); @@ -304,7 +304,7 @@ TEST(PlStringTest, FindChar) EXPECT_EQ(-1, plString().Find('A', plString::kCaseInsensitive)); } -TEST(PlStringTest, FindLast) +TEST(plString, FindLast) { // Available char, case sensitive EXPECT_EQ(0, plString("Aaaaaaaa").FindLast('A', plString::kCaseSensitive)); @@ -336,7 +336,7 @@ TEST(PlStringTest, FindLast) EXPECT_EQ(-1, plString().FindLast('A', plString::kCaseInsensitive)); } -TEST(PlStringTest, FindString) +TEST(plString, FindString) { // Available string, case sensitive EXPECT_EQ(0, plString("ABCDabcd").Find("ABCD", plString::kCaseSensitive)); @@ -383,7 +383,7 @@ TEST(PlStringTest, FindString) //TODO: test regex functions -TEST(PlStringTest, Trim) +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")); @@ -404,32 +404,34 @@ TEST(PlStringTest, Trim) EXPECT_EQ(plString(" xxx "), plString(" xxx ").Trim("x")); } -TEST(PlStringTest,Substr) +TEST(plString, Substrings) { - 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); + 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,Replace) From 76ec2e93da94190cb055e64e85d999b177c357d4 Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Sun, 5 Jul 2015 11:21:36 -0700 Subject: [PATCH 10/18] Add utility test block, and extend Replace/ToUpper/ToLower --- Sources/Tests/CoreTests/test_plString.cpp | 83 ++++++++++++++++------- 1 file changed, 59 insertions(+), 24 deletions(-) diff --git a/Sources/Tests/CoreTests/test_plString.cpp b/Sources/Tests/CoreTests/test_plString.cpp index d6c88335..8a0b79ea 100644 --- a/Sources/Tests/CoreTests/test_plString.cpp +++ b/Sources/Tests/CoreTests/test_plString.cpp @@ -68,6 +68,24 @@ TEST(plString, TestHelpers) EXPECT_LT(0, T_strcmp("a", "")); } +TEST(plString, Utility) +{ + // 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(plString, ConvertUtf8) { // From UTF-8 to plString @@ -434,34 +452,51 @@ TEST(plString, Substrings) EXPECT_EQ(plString("AAA"), plString("AAA").Substr(-10, 10)); } -TEST(PlStringTest,Replace) +TEST(plString, Replace) { - 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); - + 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")); } -TEST(PlStringTest,ToUpper) +TEST(plString, CaseConvert) { - plString input = plString("abCDe"); - plString output = input.ToUpper(); - plString expected = plString("ABCDE"); - EXPECT_EQ(expected,output); -} - -TEST(PlStringTest,ToLower) -{ - plString input = plString("aBcDe"); - plString output = input.ToLower(); - plString expected = plString("abcde"); - EXPECT_EQ(expected,output); + /* 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()); } TEST(PlStringTest,Tokenize) From e208a00db591717382e858ff3af8a050037854a5 Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Sun, 5 Jul 2015 12:00:02 -0700 Subject: [PATCH 11/18] Extend Tokenize, Split and Fill --- Sources/Tests/CoreTests/test_plString.cpp | 137 +++++++++++++++++----- 1 file changed, 106 insertions(+), 31 deletions(-) diff --git a/Sources/Tests/CoreTests/test_plString.cpp b/Sources/Tests/CoreTests/test_plString.cpp index 8a0b79ea..3d2768a1 100644 --- a/Sources/Tests/CoreTests/test_plString.cpp +++ b/Sources/Tests/CoreTests/test_plString.cpp @@ -1,5 +1,4 @@ -#include -#include +#include "plString.h" #include #include @@ -499,40 +498,116 @@ TEST(plString, CaseConvert) EXPECT_EQ(plString(""), plString("").ToLower()); } -TEST(PlStringTest,Tokenize) +TEST(plString, Tokenize) { - 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\t\tb\n;c-d;è"); - std::vector output = input.Tokenize("\t\n-;"); - EXPECT_EQ(expected,output); - + 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-;")); } -TEST(PlStringTest,Split) +TEST(plString, Split) { - 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); - + 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)); } -TEST(PlStringTest,Fill) +TEST(plString, Fill) { - plString expected = plString("aaaaa"); - plString output = plString::Fill(5,'a'); - EXPECT_EQ(expected,output); + EXPECT_EQ(plString(""), plString::Fill(0, 'a')); + EXPECT_EQ(plString("aaaaa"), plString::Fill(5, 'a')); + EXPECT_EQ(plString("aaaaaaaaaaaaaaaaaaaa"), plString::Fill(20, 'a')); } - From 19150c7e4ac7cebb9c617d53e21ae4c8a0eb084e Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Sun, 5 Jul 2015 15:07:35 -0700 Subject: [PATCH 12/18] Start adding plFormat tests, and remove assert to match plString --- Sources/Plasma/CoreLib/plFormat.cpp | 4 +- Sources/Tests/CoreTests/CMakeLists.txt | 1 + Sources/Tests/CoreTests/test_plFormat.cpp | 85 +++++++++++++++++++++++ 3 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 Sources/Tests/CoreTests/test_plFormat.cpp diff --git a/Sources/Plasma/CoreLib/plFormat.cpp b/Sources/Plasma/CoreLib/plFormat.cpp index 4126640f..abe8c465 100644 --- a/Sources/Plasma/CoreLib/plFormat.cpp +++ b/Sources/Plasma/CoreLib/plFormat.cpp @@ -294,9 +294,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); 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..39b3de38 --- /dev/null +++ b/Sources/Tests/CoreTests/test_plFormat.cpp @@ -0,0 +1,85 @@ +#include "plFormat.h" +#include "plFileSystem.h" + +#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) +{ + 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("xxтəßtxx"), plFormat("xx{}xx", L"тəßt")); + EXPECT_EQ(plString("xxтəßtxx"), plFormat("xx{}xx", plString("тəßt"))); + EXPECT_EQ(plString("xxTESTxx"), plFormat("xx{}xx", std::string("TEST"))); + EXPECT_EQ(plString("xxтəßtxx"), plFormat("xx{}xx", std::wstring(L"тəßt"))); + 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)); +} From 76372424cdcbd8ae5d777c5aae3fd750fdfc7e49 Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Sun, 5 Jul 2015 15:56:45 -0700 Subject: [PATCH 13/18] Test plFormat's decimal conversions, and fix several issues with signed decimal formatting (especially with negative values) --- Sources/Plasma/CoreLib/plFormat.cpp | 31 ++++++------ Sources/Tests/CoreTests/test_plFormat.cpp | 60 +++++++++++++++++++++++ 2 files changed, 75 insertions(+), 16 deletions(-) diff --git a/Sources/Plasma/CoreLib/plFormat.cpp b/Sources/Plasma/CoreLib/plFormat.cpp index abe8c465..1ab5c5a8 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) @@ -226,7 +227,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; @@ -250,8 +252,8 @@ 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; @@ -266,18 +268,15 @@ static void _formatDecimal(const plFormat_Private::FormatSpec &format, if (value < 0 || format.fDigitClass == plFormat_Private::kDigitDecAlwaysSigned) ++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] = '-'; + buffer[0] = '-'; else if (format.fDigitClass == plFormat_Private::kDigitDecAlwaysSigned) - buffer[signPos] = '+'; + buffer[0] = '+'; _formatString(format, output, buffer, format_size, plFormat_Private::kAlignRight); } @@ -477,20 +476,20 @@ 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/Tests/CoreTests/test_plFormat.cpp b/Sources/Tests/CoreTests/test_plFormat.cpp index 39b3de38..a15e8705 100644 --- a/Sources/Tests/CoreTests/test_plFormat.cpp +++ b/Sources/Tests/CoreTests/test_plFormat.cpp @@ -3,6 +3,7 @@ #include #include +#include TEST(plFormat, Escapes) { @@ -83,3 +84,62 @@ TEST(plFormat, Char) 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())); +} From db8d2b804448e64b19a4e7e731fcb6ed13d719bb Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Sun, 5 Jul 2015 16:56:08 -0700 Subject: [PATCH 14/18] Non-decimal int formatters --- Sources/Plasma/CoreLib/plFormat.cpp | 6 +- Sources/Tests/CoreTests/test_plFormat.cpp | 194 ++++++++++++++++++++++ 2 files changed, 197 insertions(+), 3 deletions(-) diff --git a/Sources/Plasma/CoreLib/plFormat.cpp b/Sources/Plasma/CoreLib/plFormat.cpp index 1ab5c5a8..53312dcb 100644 --- a/Sources/Plasma/CoreLib/plFormat.cpp +++ b/Sources/Plasma/CoreLib/plFormat.cpp @@ -240,9 +240,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); } @@ -256,7 +256,7 @@ static void _formatDecimal(const plFormat_Private::FormatSpec &format, _UType abs = (value < 0) ? -(_UType)value : value; size_t format_size = 0; - _IType temp = abs; + _UType temp = abs; while (temp) { ++format_size; temp /= 10; diff --git a/Sources/Tests/CoreTests/test_plFormat.cpp b/Sources/Tests/CoreTests/test_plFormat.cpp index a15e8705..a71857a8 100644 --- a/Sources/Tests/CoreTests/test_plFormat.cpp +++ b/Sources/Tests/CoreTests/test_plFormat.cpp @@ -143,3 +143,197 @@ TEST(plFormat, Decimal) 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())); +} From 890454a30f7ac7eb7f0acbebfe526c5ff5ce18a1 Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Fri, 10 Jul 2015 19:27:53 -0700 Subject: [PATCH 15/18] Support '+' format for floating-point types as well --- Sources/Plasma/CoreLib/plFormat.cpp | 17 ++++++++--------- Sources/Plasma/CoreLib/plFormat.h | 4 ++-- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/Sources/Plasma/CoreLib/plFormat.cpp b/Sources/Plasma/CoreLib/plFormat.cpp index 53312dcb..cf5ebda2 100644 --- a/Sources/Plasma/CoreLib/plFormat.cpp +++ b/Sources/Plasma/CoreLib/plFormat.cpp @@ -121,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; @@ -265,7 +264,7 @@ 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 < 24, "Format length too long"); @@ -275,7 +274,7 @@ static void _formatDecimal(const plFormat_Private::FormatSpec &format, if (value < 0) buffer[0] = '-'; - else if (format.fDigitClass == plFormat_Private::kDigitDecAlwaysSigned) + else if (format.fAlwaysSigned) buffer[0] = '+'; _formatString(format, output, buffer, format_size, plFormat_Private::kAlignRight); @@ -340,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; \ @@ -369,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; \ @@ -402,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); @@ -459,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: @@ -488,7 +488,6 @@ PL_FORMAT_IMPL(wchar_t) _formatNumeric(format, output, value, 16, true); break; case plFormat_Private::kDigitDec: - case plFormat_Private::kDigitDecAlwaysSigned: _formatDecimal(format, output, value); break; case plFormat_Private::kDigitChar: 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 */ From a6cbdc2f13da9d22557197a7532ca676e5a8c987 Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Sat, 11 Jul 2015 21:34:01 -0700 Subject: [PATCH 16/18] Add test for recently discovered bug in stack-buffer-constructed plStrings --- Sources/Tests/CoreTests/test_plString.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Sources/Tests/CoreTests/test_plString.cpp b/Sources/Tests/CoreTests/test_plString.cpp index 3d2768a1..970e2522 100644 --- a/Sources/Tests/CoreTests/test_plString.cpp +++ b/Sources/Tests/CoreTests/test_plString.cpp @@ -85,6 +85,22 @@ TEST(plString, Utility) EXPECT_EQ(utf8_test_data_length, plString(utf8_test_data).GetSize()); } +TEST(plString, StackStrings) +{ + 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(plString, ConvertUtf8) { // From UTF-8 to plString From 043b8b3b95848e260c70310ecdb3c58a61753a05 Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Sat, 11 Jul 2015 21:53:04 -0700 Subject: [PATCH 17/18] Add floating point and boolean formatting tests --- Sources/Tests/CoreTests/test_plFormat.cpp | 48 +++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/Sources/Tests/CoreTests/test_plFormat.cpp b/Sources/Tests/CoreTests/test_plFormat.cpp index a71857a8..8d6830a9 100644 --- a/Sources/Tests/CoreTests/test_plFormat.cpp +++ b/Sources/Tests/CoreTests/test_plFormat.cpp @@ -337,3 +337,51 @@ TEST(plFormat, Binary) 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)); + + // Scientific notation + EXPECT_EQ(plString("xx3.14e+00xx"), plFormat("xx{.2e}xx", 3.14159)); + EXPECT_EQ(plString("xx3.141590e+00xx"), plFormat("xx{.6e}xx", 3.14159)); + EXPECT_EQ(plString("xx1.64e+04xx"), plFormat("xx{.2e}xx", 16384.0)); + EXPECT_EQ(plString("xx7.81e-03xx"), plFormat("xx{.2e}xx", 1.0 / 128)); + + // Scientific notation (upper-case E) + EXPECT_EQ(plString("xx3.14E+00xx"), plFormat("xx{.2E}xx", 3.14159)); + EXPECT_EQ(plString("xx3.141590E+00xx"), plFormat("xx{.6E}xx", 3.14159)); + EXPECT_EQ(plString("xx1.64E+04xx"), plFormat("xx{.2E}xx", 16384.0)); + EXPECT_EQ(plString("xx7.81E-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+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)); +} From ce141a7f26bb47f60b984ffac55f7af3064b7edc Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Sat, 11 Jul 2015 22:37:43 -0700 Subject: [PATCH 18/18] Some fixes for Visual C++ --- Sources/Plasma/CoreLib/HeadSpin.cpp | 13 ++++---- Sources/Tests/CoreTests/test_plFormat.cpp | 36 +++++++++++++++-------- 2 files changed, 30 insertions(+), 19 deletions(-) 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/Tests/CoreTests/test_plFormat.cpp b/Sources/Tests/CoreTests/test_plFormat.cpp index 8d6830a9..d0d5af1d 100644 --- a/Sources/Tests/CoreTests/test_plFormat.cpp +++ b/Sources/Tests/CoreTests/test_plFormat.cpp @@ -15,6 +15,9 @@ TEST(plFormat, Escapes) 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), @@ -56,10 +59,10 @@ 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("xxтəßtxx"), plFormat("xx{}xx", L"тəßt")); - EXPECT_EQ(plString("xxтəßtxx"), plFormat("xx{}xx", plString("тəßt"))); + 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("xxтəßtxx"), plFormat("xx{}xx", std::wstring(L"тəßt"))); + 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)"))); @@ -360,22 +363,29 @@ TEST(plFormat, FloatingPoint) EXPECT_EQ(plString("xx16384.00xx"), plFormat("xx{.2f}xx", 16384.0)); EXPECT_EQ(plString("xx0.01xx"), plFormat("xx{.2f}xx", 1.0 / 128)); - // Scientific notation - EXPECT_EQ(plString("xx3.14e+00xx"), plFormat("xx{.2e}xx", 3.14159)); - EXPECT_EQ(plString("xx3.141590e+00xx"), plFormat("xx{.6e}xx", 3.14159)); - EXPECT_EQ(plString("xx1.64e+04xx"), plFormat("xx{.2e}xx", 16384.0)); - EXPECT_EQ(plString("xx7.81e-03xx"), plFormat("xx{.2e}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+00xx"), plFormat("xx{.2E}xx", 3.14159)); - EXPECT_EQ(plString("xx3.141590E+00xx"), plFormat("xx{.6E}xx", 3.14159)); - EXPECT_EQ(plString("xx1.64E+04xx"), plFormat("xx{.2E}xx", 16384.0)); - EXPECT_EQ(plString("xx7.81E-03xx"), plFormat("xx{.2E}xx", 1.0 / 128)); + 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+04xx"), plFormat("xx{.2}xx", 16384.0)); + 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)); }