From 29ebebb0b25ac201ab6a8ac0ab760881e9e183f5 Mon Sep 17 00:00:00 2001 From: grdanny Date: Sun, 22 Feb 2026 19:53:55 -0800 Subject: [PATCH 1/2] tightening compatbility with python's behavior in edge cases --- pystring_impl.h | 40 +++- test.cpp | 592 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 624 insertions(+), 8 deletions(-) diff --git a/pystring_impl.h b/pystring_impl.h index 43844a6..e6d6a48 100644 --- a/pystring_impl.h +++ b/pystring_impl.h @@ -504,11 +504,18 @@ const std::string colon = ":"; if ( len == 0 ) return false; if( len == 1 ) return ::islower( str[0] ); - for ( i = 0; i < len; ++i ) - { - if ( !::islower( str[i] ) ) return false; + // python's islower is a lot more leniant than c++ + // this will match the python behavior so that something like + // islower("hello123") is true + + bool has_cased = false; + for (i = 0; i < len; ++i) { + if (::islower(str[i])) + has_cased = true; + else if (::isupper(str[i])) + return false; } - return true; + return has_cased; } ////////////////////////////////////////////////////////////////////////////////////////////// @@ -580,11 +587,18 @@ const std::string colon = ":"; if ( len == 0 ) return false; if( len == 1 ) return ::isupper( str[0] ); - for ( i = 0; i < len; ++i ) - { - if ( !::isupper( str[i] ) ) return false; + // python's isupper is a lot more leniant than c++ + // this will match the python behavior so that something like + // isupper("HELLO123") is true + + bool has_cased = false; + for (std::string::size_type i = 0; i < str.size(); ++i) { + if (::isupper(str[i])) + has_cased = true; + else if (::islower(str[i])) + return false; } - return true; + return has_cased; } ////////////////////////////////////////////////////////////////////////////////////////////// @@ -927,6 +941,16 @@ const std::string colon = ":"; int nummatches = 0; int cursor = start; + // special handling for an empty substring + // this will match python's behavior of + // "bob".count("") == 4 + // "".count("") == 1 + if ( substr.empty() ) + { + PYSTRING_ADJUST_INDICES(start, end, (int)str.size()); + return end - start + 1; + } + while ( 1 ) { cursor = find( str, substr, cursor, end ); diff --git a/test.cpp b/test.cpp index ed702de..de2c33a 100644 --- a/test.cpp +++ b/test.cpp @@ -7,6 +7,25 @@ #include "pystring.h" #include "unittest.h" +// Helper wrappers (for pystring functions that don't have direct return values) + +namespace { +static std::vector pystring_split(const std::string& s, int maxsplit) + { std::vector r; pystring::split(s, r, "", maxsplit); return r; } +static std::vector pystring_split(const std::string& s, const std::string& sep, int maxsplit) + { std::vector r; pystring::split(s, r, sep, maxsplit); return r; } +static std::vector pystring_rsplit(const std::string& s, int maxsplit) + { std::vector r; pystring::rsplit(s, r, "", maxsplit); return r; } +static std::vector pystring_rsplit(const std::string& s, const std::string& sep, int maxsplit) + { std::vector r; pystring::rsplit(s, r, sep, maxsplit); return r; } +static std::vector pystring_partition(const std::string& s, const std::string& sep) + { std::vector r; pystring::partition(s, sep, r); return r; } +static std::vector pystring_rpartition(const std::string& s, const std::string& sep) + { std::vector r; pystring::rpartition(s, sep, r); return r; } +static std::vector pystring_splitlines(const std::string& s, bool keepends) + { std::vector r; pystring::splitlines(s, r, keepends); return r; } +} // namespace + PYSTRING_TEST_APP(PyStringUnitTests) PYSTRING_ADD_TEST(pystring, endswith) @@ -33,6 +52,17 @@ PYSTRING_ADD_TEST(pystring, endswith) PYSTRING_CHECK_EQUAL(pystring::endswith("abcdef", "cdef", -10), true); } +PYSTRING_ADD_TEST(pystring, isupper) +{ + PYSTRING_CHECK_EQUAL(pystring::isupper("ABC"), true); + PYSTRING_CHECK_EQUAL(pystring::isupper("abc"), false); + PYSTRING_CHECK_EQUAL(pystring::isupper("ABc"), false); + PYSTRING_CHECK_EQUAL(pystring::isupper("AB-C"), true); + PYSTRING_CHECK_EQUAL(pystring::isupper("HELLO 123"), true); + PYSTRING_CHECK_EQUAL(pystring::isupper("123"), false); + PYSTRING_CHECK_EQUAL(pystring::isupper("HELLO!"), true); +} + PYSTRING_ADD_TEST(pystring, find) { PYSTRING_CHECK_EQUAL(pystring::find("", ""), 0); @@ -670,3 +700,565 @@ PYSTRING_ADD_TEST(pystring_os_path, splitext) splitext_nt(root, ext, "c:\\a.b.c"); PYSTRING_CHECK_EQUAL(root, "c:\\a.b"); PYSTRING_CHECK_EQUAL(ext, ".c"); splitext_nt(root, ext, "c:\\a_b.c"); PYSTRING_CHECK_EQUAL(root, "c:\\a_b"); PYSTRING_CHECK_EQUAL(ext, ".c"); } + +// the python3_compat tests were auto generated from python +// to make sure that our functions exactly match the values that are being returned in python 3.11 + +PYSTRING_ADD_TEST(python3_compat, capitalize) +{ + PYSTRING_CHECK_EQUAL(pystring::capitalize(""), ""); + PYSTRING_CHECK_EQUAL(pystring::capitalize("hello"), "Hello"); + PYSTRING_CHECK_EQUAL(pystring::capitalize("HELLO"), "Hello"); + PYSTRING_CHECK_EQUAL(pystring::capitalize("Hello World"), "Hello world"); + PYSTRING_CHECK_EQUAL(pystring::capitalize("hello123"), "Hello123"); + PYSTRING_CHECK_EQUAL(pystring::capitalize("123"), "123"); + PYSTRING_CHECK_EQUAL(pystring::capitalize("hElLo"), "Hello"); + PYSTRING_CHECK_EQUAL(pystring::capitalize("hello world!"), "Hello world!"); +} + +PYSTRING_ADD_TEST(python3_compat, center) +{ + PYSTRING_CHECK_EQUAL(pystring::center("hello", 10), " hello "); + PYSTRING_CHECK_EQUAL(pystring::center("hello", 3), "hello"); + PYSTRING_CHECK_EQUAL(pystring::center("", 5), " "); + PYSTRING_CHECK_EQUAL(pystring::center("hello", 5), "hello"); +} + +PYSTRING_ADD_TEST(python3_compat, count_python3) +{ + PYSTRING_CHECK_EQUAL(pystring::count("", "", 0), 1); + PYSTRING_CHECK_EQUAL(pystring::count("hello world", "o", 0), 2); + PYSTRING_CHECK_EQUAL(pystring::count("hello world", "l", 0), 3); + PYSTRING_CHECK_EQUAL(pystring::count("hello world", "xyz", 0), 0); + PYSTRING_CHECK_EQUAL(pystring::count("hello world", "", 0), 12); + PYSTRING_CHECK_EQUAL(pystring::count("aaa", "a", 0), 3); + PYSTRING_CHECK_EQUAL(pystring::count("aaa", "aa", 0), 1); + PYSTRING_CHECK_EQUAL(pystring::count("hello", "l", 2, 5), 2); +} + +PYSTRING_ADD_TEST(python3_compat, endswith_python3) +{ + PYSTRING_CHECK_EQUAL(pystring::endswith("hello world", "hello", 0), false); + PYSTRING_CHECK_EQUAL(pystring::endswith("hello world", "world", 0), true); + PYSTRING_CHECK_EQUAL(pystring::endswith("hello world", "", 0), true); + PYSTRING_CHECK_EQUAL(pystring::endswith("hello world", "hello", 1), false); + PYSTRING_CHECK_EQUAL(pystring::endswith("hello world", "ello", 1), false); + PYSTRING_CHECK_EQUAL(pystring::endswith("hello", "hello world", 0), false); + PYSTRING_CHECK_EQUAL(pystring::endswith("hello world", "world", 0, 5), false); + PYSTRING_CHECK_EQUAL(pystring::endswith("hello world", "hello", 0, 5), true); + PYSTRING_CHECK_EQUAL(pystring::endswith("", "", 0), true); + PYSTRING_CHECK_EQUAL(pystring::endswith("hello", "hello", 0), true); +} + +PYSTRING_ADD_TEST(python3_compat, expandtabs) +{ + PYSTRING_CHECK_EQUAL(pystring::expandtabs("hello\tworld", 8), "hello world"); + PYSTRING_CHECK_EQUAL(pystring::expandtabs("hello\tworld", 4), "hello world"); + PYSTRING_CHECK_EQUAL(pystring::expandtabs("\t\t", 4), " "); + PYSTRING_CHECK_EQUAL(pystring::expandtabs("no tabs", 4), "no tabs"); + PYSTRING_CHECK_EQUAL(pystring::expandtabs("a\tb\tc", 4), "a b c"); +} + +PYSTRING_ADD_TEST(python3_compat, find_python3) +{ + PYSTRING_CHECK_EQUAL(pystring::find("hello world", "world", 0), 6); + PYSTRING_CHECK_EQUAL(pystring::find("hello world", "hello", 0), 0); + PYSTRING_CHECK_EQUAL(pystring::find("hello world", "xyz", 0), -1); + PYSTRING_CHECK_EQUAL(pystring::find("hello world", "", 0), 0); + PYSTRING_CHECK_EQUAL(pystring::find("hello world", "o", 0), 4); + PYSTRING_CHECK_EQUAL(pystring::find("hello world", "o", 5), 7); + PYSTRING_CHECK_EQUAL(pystring::find("hello world", "o", 0, 5), 4); + PYSTRING_CHECK_EQUAL(pystring::find("", "hello", 0), -1); + PYSTRING_CHECK_EQUAL(pystring::find("hello", "", 0), 0); + PYSTRING_CHECK_EQUAL(pystring::find("abcabc", "bc", 0), 1); + PYSTRING_CHECK_EQUAL(pystring::find("abcabc", "bc", 2), 4); +} + +PYSTRING_ADD_TEST(python3_compat, isalnum) +{ + PYSTRING_CHECK_EQUAL(pystring::isalnum(""), false); + PYSTRING_CHECK_EQUAL(pystring::isalnum("abc"), true); + PYSTRING_CHECK_EQUAL(pystring::isalnum("123"), true); + PYSTRING_CHECK_EQUAL(pystring::isalnum("abc123"), true); + PYSTRING_CHECK_EQUAL(pystring::isalnum("abc!"), false); + PYSTRING_CHECK_EQUAL(pystring::isalnum(" "), false); + PYSTRING_CHECK_EQUAL(pystring::isalnum("abc 123"), false); +} + +PYSTRING_ADD_TEST(python3_compat, isalpha) +{ + PYSTRING_CHECK_EQUAL(pystring::isalpha(""), false); + PYSTRING_CHECK_EQUAL(pystring::isalpha("abc"), true); + PYSTRING_CHECK_EQUAL(pystring::isalpha("ABC"), true); + PYSTRING_CHECK_EQUAL(pystring::isalpha("abcABC"), true); + PYSTRING_CHECK_EQUAL(pystring::isalpha("abc1"), false); + PYSTRING_CHECK_EQUAL(pystring::isalpha("abc!"), false); + PYSTRING_CHECK_EQUAL(pystring::isalpha(" "), false); + PYSTRING_CHECK_EQUAL(pystring::isalpha("a b"), false); +} + +PYSTRING_ADD_TEST(python3_compat, isdigit) +{ + PYSTRING_CHECK_EQUAL(pystring::isdigit(""), false); + PYSTRING_CHECK_EQUAL(pystring::isdigit("0"), true); + PYSTRING_CHECK_EQUAL(pystring::isdigit("123"), true); + PYSTRING_CHECK_EQUAL(pystring::isdigit("12.3"), false); + PYSTRING_CHECK_EQUAL(pystring::isdigit("12 3"), false); + PYSTRING_CHECK_EQUAL(pystring::isdigit("abc"), false); + PYSTRING_CHECK_EQUAL(pystring::isdigit("123abc"), false); + PYSTRING_CHECK_EQUAL(pystring::isdigit(" "), false); +} + +PYSTRING_ADD_TEST(python3_compat, islower) +{ + PYSTRING_CHECK_EQUAL(pystring::islower(""), false); + PYSTRING_CHECK_EQUAL(pystring::islower("a"), true); + PYSTRING_CHECK_EQUAL(pystring::islower("A"), false); + PYSTRING_CHECK_EQUAL(pystring::islower("abc"), true); + PYSTRING_CHECK_EQUAL(pystring::islower("ABC"), false); + PYSTRING_CHECK_EQUAL(pystring::islower("abC"), false); + PYSTRING_CHECK_EQUAL(pystring::islower("abc123"), true); + PYSTRING_CHECK_EQUAL(pystring::islower("123"), false); + PYSTRING_CHECK_EQUAL(pystring::islower("abc 123"), true); + PYSTRING_CHECK_EQUAL(pystring::islower("hello!"), true); + PYSTRING_CHECK_EQUAL(pystring::islower("HELLO!"), false); + PYSTRING_CHECK_EQUAL(pystring::islower(" "), false); + PYSTRING_CHECK_EQUAL(pystring::islower("abc\n"), true); + PYSTRING_CHECK_EQUAL(pystring::islower("!@#$"), false); + PYSTRING_CHECK_EQUAL(pystring::islower("a!b"), true); + PYSTRING_CHECK_EQUAL(pystring::islower("1a"), true); +} + +PYSTRING_ADD_TEST(python3_compat, isspace) +{ + PYSTRING_CHECK_EQUAL(pystring::isspace(""), false); + PYSTRING_CHECK_EQUAL(pystring::isspace(" "), true); + PYSTRING_CHECK_EQUAL(pystring::isspace(" "), true); + PYSTRING_CHECK_EQUAL(pystring::isspace("\t"), true); + PYSTRING_CHECK_EQUAL(pystring::isspace("\n"), true); + PYSTRING_CHECK_EQUAL(pystring::isspace("\r"), true); + PYSTRING_CHECK_EQUAL(pystring::isspace(" \t\n"), true); + PYSTRING_CHECK_EQUAL(pystring::isspace("a"), false); + PYSTRING_CHECK_EQUAL(pystring::isspace(" a "), false); + PYSTRING_CHECK_EQUAL(pystring::isspace(" a"), false); +} + +PYSTRING_ADD_TEST(python3_compat, istitle) +{ + PYSTRING_CHECK_EQUAL(pystring::istitle(""), false); + PYSTRING_CHECK_EQUAL(pystring::istitle("Title"), true); + PYSTRING_CHECK_EQUAL(pystring::istitle("Title Case"), true); + PYSTRING_CHECK_EQUAL(pystring::istitle("title"), false); + PYSTRING_CHECK_EQUAL(pystring::istitle("TITLE"), false); + PYSTRING_CHECK_EQUAL(pystring::istitle("Title123"), true); + PYSTRING_CHECK_EQUAL(pystring::istitle("A"), true); + PYSTRING_CHECK_EQUAL(pystring::istitle("a"), false); + PYSTRING_CHECK_EQUAL(pystring::istitle("Title Case Here"), true); + PYSTRING_CHECK_EQUAL(pystring::istitle("Title Case"), true); + PYSTRING_CHECK_EQUAL(pystring::istitle("123 Title"), true); + PYSTRING_CHECK_EQUAL(pystring::istitle("Already A Title"), true); + PYSTRING_CHECK_EQUAL(pystring::istitle("not A Title"), false); +} + +PYSTRING_ADD_TEST(python3_compat, isupper) +{ + PYSTRING_CHECK_EQUAL(pystring::isupper(""), false); + PYSTRING_CHECK_EQUAL(pystring::isupper("A"), true); + PYSTRING_CHECK_EQUAL(pystring::isupper("a"), false); + PYSTRING_CHECK_EQUAL(pystring::isupper("ABC"), true); + PYSTRING_CHECK_EQUAL(pystring::isupper("abc"), false); + PYSTRING_CHECK_EQUAL(pystring::isupper("ABc"), false); + PYSTRING_CHECK_EQUAL(pystring::isupper("ABC123"), true); + PYSTRING_CHECK_EQUAL(pystring::isupper("123"), false); + PYSTRING_CHECK_EQUAL(pystring::isupper("ABC 123"), true); + PYSTRING_CHECK_EQUAL(pystring::isupper("HELLO!"), true); + PYSTRING_CHECK_EQUAL(pystring::isupper("hello!"), false); + PYSTRING_CHECK_EQUAL(pystring::isupper(" "), false); + PYSTRING_CHECK_EQUAL(pystring::isupper("ABC\n"), true); + PYSTRING_CHECK_EQUAL(pystring::isupper("ABC\tDEF"), true); + PYSTRING_CHECK_EQUAL(pystring::isupper("!@#$"), false); + PYSTRING_CHECK_EQUAL(pystring::isupper("A!B"), true); + PYSTRING_CHECK_EQUAL(pystring::isupper("1A"), true); +} + +PYSTRING_ADD_TEST(python3_compat, join_python3) +{ + PYSTRING_CHECK_EQUAL(pystring::join(",", {}), ""); + PYSTRING_CHECK_EQUAL(pystring::join(",", {""}), ""); + PYSTRING_CHECK_EQUAL(pystring::join(",", {"a"}), "a"); + PYSTRING_CHECK_EQUAL(pystring::join(",", {"a", "b", "c"}), "a,b,c"); + PYSTRING_CHECK_EQUAL(pystring::join("", {"a", "b", "c"}), "abc"); + PYSTRING_CHECK_EQUAL(pystring::join(", ", {"hello", "world"}), "hello, world"); + PYSTRING_CHECK_EQUAL(pystring::join("-", {"one", "two", "three"}), "one-two-three"); + PYSTRING_CHECK_EQUAL(pystring::join("/", {"path", "to", "file"}), "path/to/file"); +} + +PYSTRING_ADD_TEST(python3_compat, ljust) +{ + PYSTRING_CHECK_EQUAL(pystring::ljust("hello", 10), "hello "); + PYSTRING_CHECK_EQUAL(pystring::ljust("hello", 3), "hello"); + PYSTRING_CHECK_EQUAL(pystring::ljust("", 5), " "); + PYSTRING_CHECK_EQUAL(pystring::ljust("hello", 5), "hello"); +} + +PYSTRING_ADD_TEST(python3_compat, lower) +{ + PYSTRING_CHECK_EQUAL(pystring::lower(""), ""); + PYSTRING_CHECK_EQUAL(pystring::lower("hello"), "hello"); + PYSTRING_CHECK_EQUAL(pystring::lower("HELLO"), "hello"); + PYSTRING_CHECK_EQUAL(pystring::lower("Hello World"), "hello world"); + PYSTRING_CHECK_EQUAL(pystring::lower("hello123"), "hello123"); + PYSTRING_CHECK_EQUAL(pystring::lower("123"), "123"); + PYSTRING_CHECK_EQUAL(pystring::lower("hElLo"), "hello"); + PYSTRING_CHECK_EQUAL(pystring::lower("hello world!"), "hello world!"); +} + +PYSTRING_ADD_TEST(python3_compat, lstrip) +{ + PYSTRING_CHECK_EQUAL(pystring::lstrip(""), ""); + PYSTRING_CHECK_EQUAL(pystring::lstrip(" hello "), "hello "); + PYSTRING_CHECK_EQUAL(pystring::lstrip(" hello ", " "), "hello "); + PYSTRING_CHECK_EQUAL(pystring::lstrip("xxhelloxx", "x"), "helloxx"); + PYSTRING_CHECK_EQUAL(pystring::lstrip("\t\nhello\t\n"), "hello\t\n"); + PYSTRING_CHECK_EQUAL(pystring::lstrip("hello"), "hello"); + PYSTRING_CHECK_EQUAL(pystring::lstrip(" "), ""); + PYSTRING_CHECK_EQUAL(pystring::lstrip("aabbcc", "ac"), "bbcc"); + PYSTRING_CHECK_EQUAL(pystring::lstrip("hello world "), "hello world "); +} + +PYSTRING_ADD_TEST(python3_compat, mul) +{ + PYSTRING_CHECK_EQUAL(pystring::mul("ab", 3), "ababab"); + PYSTRING_CHECK_EQUAL(pystring::mul("ab", 0), ""); + PYSTRING_CHECK_EQUAL(pystring::mul("ab", 1), "ab"); + PYSTRING_CHECK_EQUAL(pystring::mul("", 5), ""); + PYSTRING_CHECK_EQUAL(pystring::mul("x", 5), "xxxxx"); +} + + +PYSTRING_ADD_TEST(python3_compat, removeprefix_python3) +{ + PYSTRING_CHECK_EQUAL(pystring::removeprefix("hello world", "hello "), "world"); + PYSTRING_CHECK_EQUAL(pystring::removeprefix("hello world", "xyz"), "hello world"); + PYSTRING_CHECK_EQUAL(pystring::removeprefix("hello world", ""), "hello world"); + PYSTRING_CHECK_EQUAL(pystring::removeprefix("hello", "hello"), ""); + PYSTRING_CHECK_EQUAL(pystring::removeprefix("", "x"), ""); +} + +PYSTRING_ADD_TEST(python3_compat, removesuffix_python3) +{ + PYSTRING_CHECK_EQUAL(pystring::removesuffix("hello world", "hello "), "hello world"); + PYSTRING_CHECK_EQUAL(pystring::removesuffix("hello world", "xyz"), "hello world"); + PYSTRING_CHECK_EQUAL(pystring::removesuffix("hello world", ""), "hello world"); + PYSTRING_CHECK_EQUAL(pystring::removesuffix("hello", "hello"), ""); + PYSTRING_CHECK_EQUAL(pystring::removesuffix("", "x"), ""); +} + +PYSTRING_ADD_TEST(python3_compat, replace_python3) +{ + PYSTRING_CHECK_EQUAL(pystring::replace("hello world", "world", "python", -1), "hello python"); + PYSTRING_CHECK_EQUAL(pystring::replace("hello world", "o", "0", -1), "hell0 w0rld"); + PYSTRING_CHECK_EQUAL(pystring::replace("hello world", "o", "0", 1), "hell0 world"); + PYSTRING_CHECK_EQUAL(pystring::replace("hello world", "xyz", "abc", -1), "hello world"); + PYSTRING_CHECK_EQUAL(pystring::replace("hello world", "", "_", -1), "_h_e_l_l_o_ _w_o_r_l_d_"); + PYSTRING_CHECK_EQUAL(pystring::replace("aaa", "a", "bb", -1), "bbbbbb"); + PYSTRING_CHECK_EQUAL(pystring::replace("aaa", "a", "bb", 2), "bbbba"); + PYSTRING_CHECK_EQUAL(pystring::replace("", "a", "b", -1), ""); + PYSTRING_CHECK_EQUAL(pystring::replace("hello", "hello", "", -1), ""); +} + +PYSTRING_ADD_TEST(python3_compat, rfind_python3) +{ + PYSTRING_CHECK_EQUAL(pystring::rfind("hello world", "world", 0), 6); + PYSTRING_CHECK_EQUAL(pystring::rfind("hello world", "hello", 0), 0); + PYSTRING_CHECK_EQUAL(pystring::rfind("hello world", "xyz", 0), -1); + PYSTRING_CHECK_EQUAL(pystring::rfind("hello world", "", 0), 11); + PYSTRING_CHECK_EQUAL(pystring::rfind("hello world", "o", 0), 7); + PYSTRING_CHECK_EQUAL(pystring::rfind("hello world", "o", 5), 7); + PYSTRING_CHECK_EQUAL(pystring::rfind("hello world", "o", 0, 5), 4); + PYSTRING_CHECK_EQUAL(pystring::rfind("", "hello", 0), -1); + PYSTRING_CHECK_EQUAL(pystring::rfind("hello", "", 0), 5); + PYSTRING_CHECK_EQUAL(pystring::rfind("abcabc", "bc", 0), 4); + PYSTRING_CHECK_EQUAL(pystring::rfind("abcabc", "bc", 2), 4); +} + +PYSTRING_ADD_TEST(python3_compat, rjust) +{ + PYSTRING_CHECK_EQUAL(pystring::rjust("hello", 10), " hello"); + PYSTRING_CHECK_EQUAL(pystring::rjust("hello", 3), "hello"); + PYSTRING_CHECK_EQUAL(pystring::rjust("", 5), " "); + PYSTRING_CHECK_EQUAL(pystring::rjust("hello", 5), "hello"); +} + + +PYSTRING_ADD_TEST(python3_compat, rstrip) +{ + PYSTRING_CHECK_EQUAL(pystring::rstrip(""), ""); + PYSTRING_CHECK_EQUAL(pystring::rstrip(" hello "), " hello"); + PYSTRING_CHECK_EQUAL(pystring::rstrip(" hello ", " "), " hello"); + PYSTRING_CHECK_EQUAL(pystring::rstrip("xxhelloxx", "x"), "xxhello"); + PYSTRING_CHECK_EQUAL(pystring::rstrip("\t\nhello\t\n"), "\t\nhello"); + PYSTRING_CHECK_EQUAL(pystring::rstrip("hello"), "hello"); + PYSTRING_CHECK_EQUAL(pystring::rstrip(" "), ""); + PYSTRING_CHECK_EQUAL(pystring::rstrip("aabbcc", "ac"), "aabb"); + PYSTRING_CHECK_EQUAL(pystring::rstrip("hello world "), "hello world"); +} + +PYSTRING_ADD_TEST(python3_compat, slice_python3) +{ + PYSTRING_CHECK_EQUAL(pystring::slice("hello", 0, 5), "hello"); + PYSTRING_CHECK_EQUAL(pystring::slice("hello", 1, 4), "ell"); + PYSTRING_CHECK_EQUAL(pystring::slice("hello", 0, -1), "hell"); + PYSTRING_CHECK_EQUAL(pystring::slice("hello", -3, -1), "ll"); + PYSTRING_CHECK_EQUAL(pystring::slice("hello", 2, 2), ""); + PYSTRING_CHECK_EQUAL(pystring::slice("hello", 3, 1), ""); + PYSTRING_CHECK_EQUAL(pystring::slice("", 0, 0), ""); + PYSTRING_CHECK_EQUAL(pystring::slice("hello world", 6, 11), "world"); +} + + +PYSTRING_ADD_TEST(python3_compat, startswith_python3) +{ + PYSTRING_CHECK_EQUAL(pystring::startswith("hello world", "hello", 0), true); + PYSTRING_CHECK_EQUAL(pystring::startswith("hello world", "world", 0), false); + PYSTRING_CHECK_EQUAL(pystring::startswith("hello world", "", 0), true); + PYSTRING_CHECK_EQUAL(pystring::startswith("hello world", "hello", 1), false); + PYSTRING_CHECK_EQUAL(pystring::startswith("hello world", "ello", 1), true); + PYSTRING_CHECK_EQUAL(pystring::startswith("hello", "hello world", 0), false); + PYSTRING_CHECK_EQUAL(pystring::startswith("hello world", "world", 0, 5), false); + PYSTRING_CHECK_EQUAL(pystring::startswith("hello world", "hello", 0, 5), true); + PYSTRING_CHECK_EQUAL(pystring::startswith("", "", 0), true); + PYSTRING_CHECK_EQUAL(pystring::startswith("hello", "hello", 0), true); +} + +PYSTRING_ADD_TEST(python3_compat, strip) +{ + PYSTRING_CHECK_EQUAL(pystring::strip(""), ""); + PYSTRING_CHECK_EQUAL(pystring::strip(" hello "), "hello"); + PYSTRING_CHECK_EQUAL(pystring::strip(" hello ", " "), "hello"); + PYSTRING_CHECK_EQUAL(pystring::strip("xxhelloxx", "x"), "hello"); + PYSTRING_CHECK_EQUAL(pystring::strip("\t\nhello\t\n"), "hello"); + PYSTRING_CHECK_EQUAL(pystring::strip("hello"), "hello"); + PYSTRING_CHECK_EQUAL(pystring::strip(" "), ""); + PYSTRING_CHECK_EQUAL(pystring::strip("aabbcc", "ac"), "bb"); + PYSTRING_CHECK_EQUAL(pystring::strip("hello world "), "hello world"); +} + +PYSTRING_ADD_TEST(python3_compat, swapcase) +{ + PYSTRING_CHECK_EQUAL(pystring::swapcase(""), ""); + PYSTRING_CHECK_EQUAL(pystring::swapcase("hello"), "HELLO"); + PYSTRING_CHECK_EQUAL(pystring::swapcase("HELLO"), "hello"); + PYSTRING_CHECK_EQUAL(pystring::swapcase("Hello World"), "hELLO wORLD"); + PYSTRING_CHECK_EQUAL(pystring::swapcase("hello123"), "HELLO123"); + PYSTRING_CHECK_EQUAL(pystring::swapcase("123"), "123"); + PYSTRING_CHECK_EQUAL(pystring::swapcase("hElLo"), "HeLlO"); + PYSTRING_CHECK_EQUAL(pystring::swapcase("hello world!"), "HELLO WORLD!"); +} + +PYSTRING_ADD_TEST(python3_compat, title) +{ + PYSTRING_CHECK_EQUAL(pystring::title(""), ""); + PYSTRING_CHECK_EQUAL(pystring::title("hello world"), "Hello World"); + PYSTRING_CHECK_EQUAL(pystring::title("HELLO WORLD"), "Hello World"); + PYSTRING_CHECK_EQUAL(pystring::title("hello"), "Hello"); + PYSTRING_CHECK_EQUAL(pystring::title("it's a test"), "It'S A Test"); + PYSTRING_CHECK_EQUAL(pystring::title("they're bill's friends from the UK"), "They'Re Bill'S Friends From The Uk"); + PYSTRING_CHECK_EQUAL(pystring::title("hello123world"), "Hello123World"); +} + +PYSTRING_ADD_TEST(python3_compat, upper) +{ + PYSTRING_CHECK_EQUAL(pystring::upper(""), ""); + PYSTRING_CHECK_EQUAL(pystring::upper("hello"), "HELLO"); + PYSTRING_CHECK_EQUAL(pystring::upper("HELLO"), "HELLO"); + PYSTRING_CHECK_EQUAL(pystring::upper("Hello World"), "HELLO WORLD"); + PYSTRING_CHECK_EQUAL(pystring::upper("hello123"), "HELLO123"); + PYSTRING_CHECK_EQUAL(pystring::upper("123"), "123"); + PYSTRING_CHECK_EQUAL(pystring::upper("hElLo"), "HELLO"); + PYSTRING_CHECK_EQUAL(pystring::upper("hello world!"), "HELLO WORLD!"); +} + +PYSTRING_ADD_TEST(python3_compat, zfill) +{ + PYSTRING_CHECK_EQUAL(pystring::zfill("42", 5), "00042"); + PYSTRING_CHECK_EQUAL(pystring::zfill("42", 2), "42"); + PYSTRING_CHECK_EQUAL(pystring::zfill("-42", 5), "-0042"); + PYSTRING_CHECK_EQUAL(pystring::zfill("+42", 5), "+0042"); + PYSTRING_CHECK_EQUAL(pystring::zfill("hello", 8), "000hello"); + PYSTRING_CHECK_EQUAL(pystring::zfill("", 3), "000"); +} + + +PYSTRING_ADD_TEST(python3_compat, rpartition_python3) +{ + { + std::vector _e17 = {"hello", " ", "world"}; + PYSTRING_CHECK_ASSERT((pystring_rpartition("hello world", " ")) == _e17); + } + { + std::vector _e19 = {"", "", "hello world"}; + PYSTRING_CHECK_ASSERT((pystring_rpartition("hello world", "xyz")) == _e19); + } + { + std::vector _e21 = {"", "hello", ""}; + PYSTRING_CHECK_ASSERT((pystring_rpartition("hello", "hello")) == _e21); + } + { + std::vector _e23 = {"", "", ""}; + PYSTRING_CHECK_ASSERT((pystring_rpartition("", "x")) == _e23); + } + { + std::vector _e25 = {"aXb", "X", "c"}; + PYSTRING_CHECK_ASSERT((pystring_rpartition("aXbXc", "X")) == _e25); + } + { + std::vector _e27 = {"hello ", "world", ""}; + PYSTRING_CHECK_ASSERT((pystring_rpartition("hello world", "world")) == _e27); + } +} + + +PYSTRING_ADD_TEST(python3_compat, rsplit_python3) +{ + { + std::vector _e12 = {"hello world", "foo"}; + PYSTRING_CHECK_ASSERT((pystring_rsplit("hello world foo", " ", 1)) == _e12); + } + { + std::vector _e13 = {"a,b", "c", "d"}; + PYSTRING_CHECK_ASSERT((pystring_rsplit("a,b,c,d", ",", 2)) == _e13); + } + { + std::vector _e14 = {" hello", "world"}; + PYSTRING_CHECK_ASSERT((pystring_rsplit(" hello world ", 1)) == _e14); + } + { + std::vector _e15 = {"hello", "world"}; + PYSTRING_CHECK_ASSERT((pystring_rsplit("hello world", -1)) == _e15); + } +} + + +PYSTRING_ADD_TEST(python3_compat, split_python3) +{ + { + std::vector _e0 = {}; + PYSTRING_CHECK_ASSERT((pystring_split("", -1)) == _e0); + } + { + std::vector _e1 = {"hello", "world"}; + PYSTRING_CHECK_ASSERT((pystring_split("hello world", -1)) == _e1); + } + { + std::vector _e2 = {"hello", "world"}; + PYSTRING_CHECK_ASSERT((pystring_split(" hello world ", -1)) == _e2); + } + { + std::vector _e3 = {"hello", "world"}; + PYSTRING_CHECK_ASSERT((pystring_split("hello world", " ", -1)) == _e3); + } + { + std::vector _e4 = {"hello world"}; + PYSTRING_CHECK_ASSERT((pystring_split("hello world", " ", 0)) == _e4); + } + { + std::vector _e5 = {"hello", "world foo"}; + PYSTRING_CHECK_ASSERT((pystring_split("hello world foo", " ", 1)) == _e5); + } + { + std::vector _e6 = {"a", "b", "c", "d"}; + PYSTRING_CHECK_ASSERT((pystring_split("a,b,c,d", ",", -1)) == _e6); + } + { + std::vector _e7 = {"a", "b", "c,d"}; + PYSTRING_CHECK_ASSERT((pystring_split("a,b,c,d", ",", 2)) == _e7); + } + { + std::vector _e8 = {"a", "b", "c"}; + PYSTRING_CHECK_ASSERT((pystring_split("aXXbXXc", "XX", -1)) == _e8); + } + { + std::vector _e9 = {"hello"}; + PYSTRING_CHECK_ASSERT((pystring_split("hello", "x", -1)) == _e9); + } + { + std::vector _e10 = {}; + PYSTRING_CHECK_ASSERT((pystring_split(" ", -1)) == _e10); + } + { + std::vector _e11 = {"hello", "world"}; + PYSTRING_CHECK_ASSERT((pystring_split("\thello\tworld\t", -1)) == _e11); + } +} + + + +PYSTRING_ADD_TEST(python3_compat, splitlines) +{ + { + std::vector _e28 = {}; + PYSTRING_CHECK_ASSERT((pystring_splitlines("", false)) == _e28); + } + { + std::vector _e29 = {}; + PYSTRING_CHECK_ASSERT((pystring_splitlines("", true)) == _e29); + } + { + std::vector _e30 = {"hello"}; + PYSTRING_CHECK_ASSERT((pystring_splitlines("hello", false)) == _e30); + } + { + std::vector _e31 = {"hello"}; + PYSTRING_CHECK_ASSERT((pystring_splitlines("hello", true)) == _e31); + } + { + std::vector _e32 = {"hello", "world"}; + PYSTRING_CHECK_ASSERT((pystring_splitlines("hello\nworld", false)) == _e32); + } + { + std::vector _e33 = {"hello\n", "world"}; + PYSTRING_CHECK_ASSERT((pystring_splitlines("hello\nworld", true)) == _e33); + } + { + std::vector _e34 = {"hello", "world"}; + PYSTRING_CHECK_ASSERT((pystring_splitlines("hello\r\nworld", false)) == _e34); + } + { + std::vector _e35 = {"hello\r\n", "world"}; + PYSTRING_CHECK_ASSERT((pystring_splitlines("hello\r\nworld", true)) == _e35); + } + { + std::vector _e36 = {"hello", "world"}; + PYSTRING_CHECK_ASSERT((pystring_splitlines("hello\rworld", false)) == _e36); + } + { + std::vector _e37 = {"hello\r", "world"}; + PYSTRING_CHECK_ASSERT((pystring_splitlines("hello\rworld", true)) == _e37); + } + { + std::vector _e38 = {"line1", "line2", "line3"}; + PYSTRING_CHECK_ASSERT((pystring_splitlines("line1\nline2\nline3", false)) == _e38); + } + { + std::vector _e39 = {"line1\n", "line2\n", "line3"}; + PYSTRING_CHECK_ASSERT((pystring_splitlines("line1\nline2\nline3", true)) == _e39); + } + { + std::vector _e40 = {"hello"}; + PYSTRING_CHECK_ASSERT((pystring_splitlines("hello\n", false)) == _e40); + } + { + std::vector _e41 = {"hello\n"}; + PYSTRING_CHECK_ASSERT((pystring_splitlines("hello\n", true)) == _e41); + } + { + std::vector _e42 = {"", "hello"}; + PYSTRING_CHECK_ASSERT((pystring_splitlines("\nhello", false)) == _e42); + } + { + std::vector _e43 = {"\n", "hello"}; + PYSTRING_CHECK_ASSERT((pystring_splitlines("\nhello", true)) == _e43); + } +} + From b7060fd04f2aebff681ef3c31397f11c30aba8f3 Mon Sep 17 00:00:00 2001 From: grdanny Date: Mon, 23 Feb 2026 09:11:31 -0800 Subject: [PATCH 2/2] a couple of more edge cases --- test.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test.cpp b/test.cpp index de2c33a..8c59046 100644 --- a/test.cpp +++ b/test.cpp @@ -727,6 +727,8 @@ PYSTRING_ADD_TEST(python3_compat, center) PYSTRING_ADD_TEST(python3_compat, count_python3) { PYSTRING_CHECK_EQUAL(pystring::count("", "", 0), 1); + PYSTRING_CHECK_EQUAL(pystring::count("bob", ""), 4); + PYSTRING_CHECK_EQUAL(pystring::count("", "bob"), 0); PYSTRING_CHECK_EQUAL(pystring::count("hello world", "o", 0), 2); PYSTRING_CHECK_EQUAL(pystring::count("hello world", "l", 0), 3); PYSTRING_CHECK_EQUAL(pystring::count("hello world", "xyz", 0), 0);