From d810f01003643baba2313c5e6fe17dfb5deed47a Mon Sep 17 00:00:00 2001 From: resserops Date: Thu, 4 Jun 2026 15:00:03 +0800 Subject: [PATCH] fix(common/str): correct handling of empty tokens in SplitView --- module/common/include/oops/str.h | 31 ++- module/common/include/oops/view.h | 13 +- module/common/src/str.cpp | 22 +- module/common/test/test_str.cpp | 398 ++++++++++++++++++---------- module/meta/test/test_type_list.cpp | 2 +- 5 files changed, 296 insertions(+), 170 deletions(-) diff --git a/module/common/include/oops/str.h b/module/common/include/oops/str.h index 6c11689..f6039d9 100644 --- a/module/common/include/oops/str.h +++ b/module/common/include/oops/str.h @@ -1,6 +1,8 @@ #pragma once +#include #include +#include #include #include #include @@ -21,6 +23,19 @@ constexpr bool IsSpace(char c) noexcept { constexpr char ToUpper(char c) noexcept { return IsLower(c) ? (c - 'a' + 'A') : c; } constexpr char ToLower(char c) noexcept { return IsUpper(c) ? (c - 'A' + 'a') : c; } +namespace detail { +constexpr auto CHAR_MAP = [] { + constexpr std::size_t n{std::numeric_limits::max() + 1}; + std::array res{}; + for (std::size_t i{0}; i < n; ++i) { + res[i] = static_cast(i); + } + return res; +}(); +} // namespace detail + +constexpr std::string_view ToSv(char ch) noexcept { return {&detail::CHAR_MAP[static_cast(ch)], 1}; } + // 字符串读操作 std::string SplitBack(const std::string &str, const std::string &delim); // SplitView实现逆序迭代器后合并到Split中 @@ -49,17 +64,16 @@ class SplitView { bool operator==(const Iterator &other) const { // 短路和迭代器的比较 - if ((s_.data() == nullptr) ^ (other.s_.data() == nullptr)) { + if ((token_.data() == nullptr) ^ (other.token_.data() == nullptr)) { return false; // 一个迭代器是尾迭代器,另一个不是 } - if (s_.data() == nullptr) { + if (token_.data() == nullptr) { return true; // 均是尾迭代器 } // 非尾迭代器的严格匹配 - return (s_.data() == other.s_.data()) && (s_.size() == other.s_.size()) && - (token_.data() == other.token_.data()) && (token_.size() == other.token_.size()) && - (delim_ == other.delim_) && (any_of_delims_ == other.any_of_delims_) && - (skip_empty_ == other.skip_empty_); + return (token_.data() == other.token_.data()) && (token_.size() == other.token_.size()) && + (s_.data() == other.s_.data()) && (s_.size() == other.s_.size()) && (delim_ == other.delim_) && + (any_of_delims_ == other.any_of_delims_) && (skip_empty_ == other.skip_empty_); } bool operator!=(const Iterator &other) const { return !operator==(other); } @@ -79,8 +93,8 @@ class SplitView { using const_iterator = Iterator; using iterator = Iterator; - SplitView(std::string_view s) : s_{s}, delim_{SPACE}, any_of_delims_{true}, skip_empty_{true} {} - SplitView(std::string_view s, char delim) : s_{s}, delim_(&c_, 1), c_{delim} {} + explicit SplitView(std::string_view s) : s_{s}, delim_{SPACE}, any_of_delims_{true}, skip_empty_{true} {} + SplitView(std::string_view s, char delim) : s_{s}, delim_{ToSv(delim)} {} SplitView(std::string_view s, std::string_view delim) : s_{s}, delim_{delim} {} Iterator begin() const { return Iterator(s_, delim_, any_of_delims_, skip_empty_); } @@ -114,7 +128,6 @@ class SplitView { const std::string_view delim_; bool any_of_delims_{}; bool skip_empty_{}; - const char c_{}; // 支持单字符delim }; inline SplitView Split(std::string_view s) { return SplitView(s); } diff --git a/module/common/include/oops/view.h b/module/common/include/oops/view.h index dae9602..71af185 100644 --- a/module/common/include/oops/view.h +++ b/module/common/include/oops/view.h @@ -140,13 +140,14 @@ class RangeView { T step_{1}; }; -template +template , int> = 0> constexpr auto Range(T start, Stop stop, T step = 1) { - if constexpr (std::is_integral_v) { - return RangeView(start, stop, step); - } else { - return RangeView(start, stop, step); - } + return RangeView(start, stop, step); +} + +template +constexpr auto Range(T start, T stop, T step = 1) { + return RangeView(start, stop, step); } template , int> = 0> diff --git a/module/common/src/str.cpp b/module/common/src/str.cpp index bfb89a3..8870e8b 100644 --- a/module/common/src/str.cpp +++ b/module/common/src/str.cpp @@ -10,28 +10,30 @@ std::string SplitBack(const std::string &str, const std::string &delim) { } void SplitView::Iterator::NextToken() { - if (s_.data() == nullptr) { - return; - } - do { + if (s_.data() == nullptr) { + token_ = {}; + continue; + } + if (s_.empty()) { + token_ = s_; s_ = {}; - return; + continue; } - std::size_t pos; + std::size_t pos{}; if (any_of_delims_) { pos = s_.find_first_of(delim_); - } else if (delim_.empty()) { - pos = 1; + } else if (delim_.empty()) { // 特殊分支,空delim的逐字符分割 + pos = (s_.size() > 1) ? 1 : std::string_view::npos; } else { pos = s_.find(delim_); } if (pos == std::string_view::npos) { token_ = s_; - s_.remove_prefix(s_.size()); + s_ = {}; } else { token_ = s_.substr(0, pos); if (any_of_delims_) { @@ -40,7 +42,7 @@ void SplitView::Iterator::NextToken() { s_.remove_prefix(pos + delim_.size()); } } - } while (skip_empty_ && token_.empty() && s_.data() != nullptr); + } while (skip_empty_ && token_.empty() && token_.data() != nullptr); } std::string ToUpper(std::string_view s) noexcept { diff --git a/module/common/test/test_str.cpp b/module/common/test/test_str.cpp index 11e4586..310c703 100644 --- a/module/common/test/test_str.cpp +++ b/module/common/test/test_str.cpp @@ -5,207 +5,185 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" -template -constexpr auto CONSTEXPR_VALUE{Value}; +#define TEST_STATIC(suite, case) constexpr void TestStatic_##suite_##case () +using namespace testing; -#define CEXPR(expr) (CONSTEXPR_VALUE) // expr must be eval at compile-time +static std::string FmtChar(char c) { + int c_int{static_cast(c)}; + std::stringstream ss; + ss << "Char: 0x" << std::hex << c_int; + if (std::isprint(c_int)) { + ss << " '" << c << "'"; + } + return ss.str(); +} -#define TEST_STATIC(suite, case) constexpr void TestStatic_##suite_##case () +TEST_STATIC(CommonStr, IsUpper) { + static_assert(oops::IsUpper('A')); + static_assert(oops::IsUpper('Z')); -using namespace testing; + static_assert(!oops::IsUpper('a')); + static_assert(!oops::IsUpper('z')); + static_assert(!oops::IsUpper('@')); + static_assert(!oops::IsUpper('[')); -TEST(CommonStr, IsUpper) { - using namespace oops; - EXPECT_TRUE(CEXPR(IsUpper('A'))); - EXPECT_TRUE(CEXPR(IsUpper('Z'))); - - EXPECT_FALSE(CEXPR(IsUpper('a'))); - EXPECT_FALSE(CEXPR(IsUpper('z'))); - EXPECT_FALSE(CEXPR(IsUpper('@'))); - EXPECT_FALSE(CEXPR(IsUpper('['))); - - EXPECT_FALSE(CEXPR(IsUpper('0'))); - EXPECT_FALSE(CEXPR(IsUpper(' '))); - EXPECT_FALSE(CEXPR(IsUpper('\0'))); - EXPECT_FALSE(CEXPR(IsUpper('\n'))); + static_assert(!oops::IsUpper('0')); + static_assert(!oops::IsUpper(' ')); + static_assert(!oops::IsUpper('\0')); + static_assert(!oops::IsUpper('\n')); } TEST(CommonStr, IsUpperSameAsStd) { for (char c : oops::view::IntegerSet) { int c_int{static_cast(c)}; - // TODO(resserops): 后续16进制通过formatter控制 - EXPECT_EQ(oops::IsUpper(c), std::isupper(c_int) != 0) - << "Char: 0x" << std::hex << c_int << (std::isprint(c_int) ? std::string{" '"} + c + "'" : ""); + EXPECT_EQ(oops::IsUpper(c), std::isupper(c_int) != 0) << FmtChar(c); } } -TEST(CommonStr, IsLower) { - using namespace oops; - EXPECT_TRUE(CEXPR(IsLower('a'))); - EXPECT_TRUE(CEXPR(IsLower('z'))); - - EXPECT_FALSE(CEXPR(IsLower('A'))); - EXPECT_FALSE(CEXPR(IsLower('Z'))); - EXPECT_FALSE(CEXPR(IsLower('`'))); - EXPECT_FALSE(CEXPR(IsLower('{'))); - - EXPECT_FALSE(CEXPR(IsLower('0'))); - EXPECT_FALSE(CEXPR(IsLower(' '))); - EXPECT_FALSE(CEXPR(IsLower('\0'))); - EXPECT_FALSE(CEXPR(IsLower('\n'))); +TEST_STATIC(CommonStr, IsLower) { + static_assert(oops::IsLower('a')); + static_assert(oops::IsLower('z')); + + static_assert(!oops::IsLower('A')); + static_assert(!oops::IsLower('Z')); + static_assert(!oops::IsLower('`')); + static_assert(!oops::IsLower('{')); + + static_assert(!oops::IsLower('0')); + static_assert(!oops::IsLower(' ')); + static_assert(!oops::IsLower('\0')); + static_assert(!oops::IsLower('\n')); } TEST(CommonStr, IsLowerSameAsStd) { for (char c : oops::view::IntegerSet) { int c_int{static_cast(c)}; - // TODO(resserops): 后续16进制通过formatter控制 - EXPECT_EQ(oops::IsLower(c), std::islower(c_int) != 0) - << "Char: 0x" << std::hex << c_int << (std::isprint(c_int) ? std::string{" '"} + c + "'" : ""); + EXPECT_EQ(oops::IsLower(c), std::islower(c_int) != 0) << FmtChar(c); } } -TEST(CommonStr, IsAlpha) { - using namespace oops; - EXPECT_TRUE(CEXPR(IsAlpha('A'))); - EXPECT_TRUE(CEXPR(IsAlpha('Z'))); - EXPECT_TRUE(CEXPR(IsAlpha('a'))); - EXPECT_TRUE(CEXPR(IsAlpha('z'))); - - EXPECT_FALSE(CEXPR(IsAlpha('@'))); - EXPECT_FALSE(CEXPR(IsAlpha('['))); - EXPECT_FALSE(CEXPR(IsAlpha('`'))); - EXPECT_FALSE(CEXPR(IsAlpha('{'))); - - EXPECT_FALSE(CEXPR(IsAlpha('0'))); - EXPECT_FALSE(CEXPR(IsAlpha(' '))); - EXPECT_FALSE(CEXPR(IsAlpha('\0'))); - EXPECT_FALSE(CEXPR(IsAlpha('\n'))); +TEST_STATIC(CommonStr, IsAlpha) { + static_assert(oops::IsAlpha('A')); + static_assert(oops::IsAlpha('Z')); + static_assert(oops::IsAlpha('a')); + static_assert(oops::IsAlpha('z')); + + static_assert(!oops::IsAlpha('@')); + static_assert(!oops::IsAlpha('[')); + static_assert(!oops::IsAlpha('`')); + static_assert(!oops::IsAlpha('{')); + + static_assert(!oops::IsAlpha('0')); + static_assert(!oops::IsAlpha(' ')); + static_assert(!oops::IsAlpha('\0')); + static_assert(!oops::IsAlpha('\n')); } TEST(CommonStr, IsAlphaSameAsStd) { for (char c : oops::view::IntegerSet) { int c_int{static_cast(c)}; - // TODO(resserops): 后续16进制通过formatter控制 - EXPECT_EQ(oops::IsAlpha(c), std::isalpha(c_int) != 0) - << "Char: 0x" << std::hex << c_int << (std::isprint(c_int) ? std::string{" '"} + c + "'" : ""); + EXPECT_EQ(oops::IsAlpha(c), std::isalpha(c_int) != 0) << FmtChar(c); } } -TEST(CommonStr, IsDigit) { - using namespace oops; - EXPECT_TRUE(CEXPR(IsDigit('0'))); - EXPECT_TRUE(CEXPR(IsDigit('9'))); +TEST_STATIC(CommonStr, IsDigit) { + static_assert(oops::IsDigit('0')); + static_assert(oops::IsDigit('9')); - EXPECT_FALSE(CEXPR(IsDigit('/'))); - EXPECT_FALSE(CEXPR(IsDigit(':'))); + static_assert(!oops::IsDigit('/')); + static_assert(!oops::IsDigit(':')); - EXPECT_FALSE(CEXPR(IsDigit('A'))); - EXPECT_FALSE(CEXPR(IsDigit('a'))); - EXPECT_FALSE(CEXPR(IsDigit(' '))); - EXPECT_FALSE(CEXPR(IsDigit('\0'))); - EXPECT_FALSE(CEXPR(IsDigit('\n'))); + static_assert(!oops::IsDigit('A')); + static_assert(!oops::IsDigit('a')); + static_assert(!oops::IsDigit(' ')); + static_assert(!oops::IsDigit('\0')); + static_assert(!oops::IsDigit('\n')); } TEST(CommonStr, IsDigitSameAsStd) { for (char c : oops::view::IntegerSet) { int c_int{static_cast(c)}; - // TODO(resserops): 后续16进制通过formatter控制 - EXPECT_EQ(oops::IsDigit(c), std::isdigit(c_int) != 0) - << "Char: 0x" << std::hex << c_int << (std::isprint(c_int) ? std::string{" '"} + c + "'" : ""); + EXPECT_EQ(oops::IsDigit(c), std::isdigit(c_int) != 0) << FmtChar(c); } } -TEST(CommonStr, IsAlnum) { - using namespace oops; - EXPECT_TRUE(CEXPR(IsAlnum('A'))); - EXPECT_TRUE(CEXPR(IsAlnum('Z'))); - EXPECT_TRUE(CEXPR(IsAlnum('a'))); - EXPECT_TRUE(CEXPR(IsAlnum('z'))); - EXPECT_TRUE(CEXPR(IsAlnum('0'))); - EXPECT_TRUE(CEXPR(IsAlnum('9'))); - - EXPECT_FALSE(CEXPR(IsAlnum('@'))); - EXPECT_FALSE(CEXPR(IsAlnum('['))); - EXPECT_FALSE(CEXPR(IsAlnum('`'))); - EXPECT_FALSE(CEXPR(IsAlnum('{'))); - EXPECT_FALSE(CEXPR(IsAlnum('/'))); - EXPECT_FALSE(CEXPR(IsAlnum(':'))); - - EXPECT_FALSE(CEXPR(IsAlnum(' '))); - EXPECT_FALSE(CEXPR(IsAlnum('\0'))); - EXPECT_FALSE(CEXPR(IsAlnum('\n'))); +TEST_STATIC(CommonStr, IsAlnum) { + static_assert(oops::IsAlnum('A')); + static_assert(oops::IsAlnum('Z')); + static_assert(oops::IsAlnum('a')); + static_assert(oops::IsAlnum('z')); + static_assert(oops::IsAlnum('0')); + static_assert(oops::IsAlnum('9')); + + static_assert(!oops::IsAlnum('@')); + static_assert(!oops::IsAlnum('[')); + static_assert(!oops::IsAlnum('`')); + static_assert(!oops::IsAlnum('{')); + static_assert(!oops::IsAlnum('/')); + static_assert(!oops::IsAlnum(':')); + + static_assert(!oops::IsAlnum(' ')); + static_assert(!oops::IsAlnum('\0')); + static_assert(!oops::IsAlnum('\n')); } TEST(CommonStr, IsAlnumSameAsStd) { - std::size_t count{0}; for (char c : oops::view::IntegerSet) { int c_int{static_cast(c)}; - // TODO(resserops): 后续16进制通过formatter控制 - EXPECT_EQ(oops::IsAlnum(c), std::isalnum(c_int) != 0) - << "Char: 0x" << std::hex << c_int << (std::isprint(c_int) ? std::string{" '"} + c + "'" : ""); - ++count; + EXPECT_EQ(oops::IsAlnum(c), std::isalnum(c_int) != 0) << FmtChar(c); } - EXPECT_EQ(count, 1ull << __CHAR_BIT__); } -TEST(CommonStr, ToLowerConstexpr) { - using namespace oops; - EXPECT_EQ(CEXPR(ToLower('A')), 'a'); - EXPECT_EQ(CEXPR(ToLower('Z')), 'z'); - - EXPECT_EQ(CEXPR(ToLower('a')), 'a'); - EXPECT_EQ(CEXPR(ToLower('z')), 'z'); - EXPECT_EQ(CEXPR(ToLower('@')), '@'); - EXPECT_EQ(CEXPR(ToLower('[')), '['); - - EXPECT_EQ(CEXPR(ToLower('0')), '0'); - EXPECT_EQ(CEXPR(ToLower(' ')), ' '); - EXPECT_EQ(CEXPR(ToLower('\0')), '\0'); - EXPECT_EQ(CEXPR(ToLower('\n')), '\n'); +TEST_STATIC(CommonStr, ToLower) { + static_assert(oops::ToLower('A') == 'a'); + static_assert(oops::ToLower('Z') == 'z'); + + static_assert(oops::ToLower('a') == 'a'); + static_assert(oops::ToLower('z') == 'z'); + static_assert(oops::ToLower('@') == '@'); + static_assert(oops::ToLower('[') == '['); + + static_assert(oops::ToLower('0') == '0'); + static_assert(oops::ToLower(' ') == ' '); + static_assert(oops::ToLower('\0') == '\0'); + static_assert(oops::ToLower('\n') == '\n'); } TEST(CommonStr, ToLowerSameAsStd) { for (char c : oops::view::IntegerSet) { int c_int{static_cast(c)}; - // TODO(resserops): 后续16进制通过formatter控制 - EXPECT_EQ(oops::ToLower(c), static_cast(std::tolower(c_int))) - << "Char: 0x" << std::hex << c_int << (std::isprint(c_int) ? std::string{" '"} + c + "'" : ""); + EXPECT_EQ(oops::ToLower(c), static_cast(std::tolower(c_int))) << FmtChar(c); } } -TEST(CommonStr, ToUpper) { - using namespace oops; - EXPECT_EQ(CEXPR(ToLower('a')), 'a'); - EXPECT_EQ(CEXPR(ToLower('z')), 'z'); - - EXPECT_EQ(CEXPR(ToLower('A')), 'a'); - EXPECT_EQ(CEXPR(ToLower('Z')), 'z'); - EXPECT_EQ(CEXPR(ToLower('`')), '`'); - EXPECT_EQ(CEXPR(ToLower('{')), '{'); - - EXPECT_EQ(CEXPR(ToLower('0')), '0'); - EXPECT_EQ(CEXPR(ToLower(' ')), ' '); - EXPECT_EQ(CEXPR(ToLower('\0')), '\0'); - EXPECT_EQ(CEXPR(ToLower('\n')), '\n'); +TEST_STATIC(CommonStr, ToUpper) { + static_assert(oops::ToUpper('a') == 'A'); + static_assert(oops::ToUpper('z') == 'Z'); + + static_assert(oops::ToUpper('A') == 'A'); + static_assert(oops::ToUpper('Z') == 'Z'); + static_assert(oops::ToUpper('`') == '`'); + static_assert(oops::ToUpper('{') == '{'); + + static_assert(oops::ToUpper('0') == '0'); + static_assert(oops::ToUpper(' ') == ' '); + static_assert(oops::ToUpper('\0') == '\0'); + static_assert(oops::ToUpper('\n') == '\n'); } TEST(CommonStr, ToUpperSameAsStd) { for (char c : oops::view::IntegerSet) { int c_int{static_cast(c)}; - // TODO(resserops): 后续16进制通过formatter控制 - EXPECT_EQ(oops::ToUpper(c), static_cast(std::toupper(c_int))) - << "Char: 0x" << std::hex << c_int << (std::isprint(c_int) ? std::string{" '"} + c + "'" : ""); + EXPECT_EQ(oops::ToUpper(c), static_cast(std::toupper(c_int))) << FmtChar(c); } } -TEST(CommonStr, Repeat) { - using namespace oops; - EXPECT_EQ(Repeat("abc", 0), ""); - EXPECT_EQ(Repeat("abc", 1), "abc"); - EXPECT_EQ(Repeat("abc", 3), "abcabcabc"); - EXPECT_EQ(Repeat("", 0), ""); - EXPECT_EQ(Repeat("", 1), ""); - EXPECT_EQ(Repeat("", 3), ""); +TEST(CommonStr, ToSv) { + for (char c : oops::view::IntegerSet) { + std::string_view sv{oops::ToSv(c)}; + EXPECT_EQ(sv.size(), 1); + EXPECT_EQ(sv.front(), c); + } } TEST(CommonStr, SplitBack) { @@ -221,16 +199,148 @@ TEST(CommonStr, SplitBack) { TEST(CommonStr, Split) { EXPECT_THAT(oops::Split(""), ElementsAre()); EXPECT_THAT(oops::Split(" "), ElementsAre()); - EXPECT_THAT(oops::Split(" "), ElementsAre()); + EXPECT_THAT(oops::Split(" "), ElementsAre()); EXPECT_THAT(oops::Split("abc"), ElementsAre("abc")); - EXPECT_THAT(oops::Split("abc defg hi"), ElementsAre("abc", "defg", "hi")); - EXPECT_THAT(oops::Split(" abc defg hi "), ElementsAre("abc", "defg", "hi")); - EXPECT_THAT(oops::Split("\nabc \t defg\fhi "), ElementsAre("abc", "defg", "hi")); + EXPECT_THAT(oops::Split("abc "), ElementsAre("abc")); + EXPECT_THAT(oops::Split("abc "), ElementsAre("abc")); + EXPECT_THAT(oops::Split(" abc"), ElementsAre("abc")); + EXPECT_THAT(oops::Split(" abc"), ElementsAre("abc")); + EXPECT_THAT(oops::Split("abc def"), ElementsAre("abc", "def")); + EXPECT_THAT(oops::Split("abc def"), ElementsAre("abc", "def")); + EXPECT_THAT(oops::Split("abc def ghi"), ElementsAre("abc", "def", "ghi")); +} + +TEST(CommonStr, SplitWhitespace) { + EXPECT_THAT(oops::Split("\t"), ElementsAre()); + EXPECT_THAT(oops::Split("\n\r"), ElementsAre()); + EXPECT_THAT(oops::Split("abc\v"), ElementsAre("abc")); + EXPECT_THAT(oops::Split("abc\f\t"), ElementsAre("abc")); + EXPECT_THAT(oops::Split("\nabc"), ElementsAre("abc")); + EXPECT_THAT(oops::Split("\r\vabc"), ElementsAre("abc")); + EXPECT_THAT(oops::Split("abc\fdef"), ElementsAre("abc", "def")); + EXPECT_THAT(oops::Split("abc\t\ndef"), ElementsAre("abc", "def")); + EXPECT_THAT(oops::Split("abc\rdef\vghi"), ElementsAre("abc", "def", "ghi")); } TEST(CommonStr, SplitDelim) { + EXPECT_THAT(oops::Split("", ":"), ElementsAre("")); + EXPECT_THAT(oops::Split(":", ":"), ElementsAre("", "")); + EXPECT_THAT(oops::Split("::", ":"), ElementsAre("", "", "")); + EXPECT_THAT(oops::Split("abc", ":"), ElementsAre("abc")); + EXPECT_THAT(oops::Split("abc:", ":"), ElementsAre("abc", "")); + EXPECT_THAT(oops::Split("abc::", ":"), ElementsAre("abc", "", "")); + EXPECT_THAT(oops::Split(":abc", ":"), ElementsAre("", "abc")); + EXPECT_THAT(oops::Split("::abc", ":"), ElementsAre("", "", "abc")); + EXPECT_THAT(oops::Split("abc:def", ":"), ElementsAre("abc", "def")); + EXPECT_THAT(oops::Split("abc::def", ":"), ElementsAre("abc", "", "def")); + EXPECT_THAT(oops::Split("abc:def:ghi", ":"), ElementsAre("abc", "def", "ghi")); +} + +TEST(CommonStr, SplitCharDelim) { + EXPECT_THAT(oops::Split("", ':'), ElementsAre("")); + EXPECT_THAT(oops::Split(":", ':'), ElementsAre("", "")); + EXPECT_THAT(oops::Split("::", ':'), ElementsAre("", "", "")); + EXPECT_THAT(oops::Split("abc", ':'), ElementsAre("abc")); + EXPECT_THAT(oops::Split("abc:", ':'), ElementsAre("abc", "")); + EXPECT_THAT(oops::Split("abc::", ':'), ElementsAre("abc", "", "")); + EXPECT_THAT(oops::Split(":abc", ':'), ElementsAre("", "abc")); + EXPECT_THAT(oops::Split("::abc", ':'), ElementsAre("", "", "abc")); + EXPECT_THAT(oops::Split("abc:def", ':'), ElementsAre("abc", "def")); + EXPECT_THAT(oops::Split("abc::def", ':'), ElementsAre("abc", "", "def")); + EXPECT_THAT(oops::Split("abc:def:ghi", ':'), ElementsAre("abc", "def", "ghi")); +} + +TEST(CommonStr, SplitMultiDelims) { + EXPECT_THAT(oops::Split("", "::"), ElementsAre("")); + EXPECT_THAT(oops::Split(":", "::"), ElementsAre(":")); + EXPECT_THAT(oops::Split("::", "::"), ElementsAre("", "")); + EXPECT_THAT(oops::Split(":::", "::"), ElementsAre("", ":")); + EXPECT_THAT(oops::Split("::::", "::"), ElementsAre("", "", "")); + EXPECT_THAT(oops::Split("abc", "::"), ElementsAre("abc")); + EXPECT_THAT(oops::Split("abc:", "::"), ElementsAre("abc:")); + EXPECT_THAT(oops::Split("abc::", "::"), ElementsAre("abc", "")); + EXPECT_THAT(oops::Split("abc:::", "::"), ElementsAre("abc", ":")); + EXPECT_THAT(oops::Split("abc::::", "::"), ElementsAre("abc", "", "")); + EXPECT_THAT(oops::Split(":abc", "::"), ElementsAre(":abc")); + EXPECT_THAT(oops::Split("::abc", "::"), ElementsAre("", "abc")); + EXPECT_THAT(oops::Split(":::abc", "::"), ElementsAre("", ":abc")); + EXPECT_THAT(oops::Split("::::abc", "::"), ElementsAre("", "", "abc")); + EXPECT_THAT(oops::Split("abc:def", "::"), ElementsAre("abc:def")); + EXPECT_THAT(oops::Split("abc::def", "::"), ElementsAre("abc", "def")); + EXPECT_THAT(oops::Split("abc:::def", "::"), ElementsAre("abc", ":def")); + EXPECT_THAT(oops::Split("abc::::def", "::"), ElementsAre("abc", "", "def")); + EXPECT_THAT(oops::Split("abc::def::ghi", "::"), ElementsAre("abc", "def", "ghi")); +} + +TEST(CommonStr, SplitAnyOfDelims) { + EXPECT_THAT(oops::Split("", ":;").AnyOfDelims(), ElementsAre("")); + EXPECT_THAT(oops::Split(":", ":;").AnyOfDelims(), ElementsAre("", "")); + EXPECT_THAT(oops::Split(";", ":;").AnyOfDelims(), ElementsAre("", "")); + EXPECT_THAT(oops::Split(":;", ":;").AnyOfDelims(), ElementsAre("", "", "")); + EXPECT_THAT(oops::Split("abc", ":;").AnyOfDelims(), ElementsAre("abc")); + EXPECT_THAT(oops::Split("abc:", ":;").AnyOfDelims(), ElementsAre("abc", "")); + EXPECT_THAT(oops::Split("abc;", ":;").AnyOfDelims(), ElementsAre("abc", "")); + EXPECT_THAT(oops::Split("abc:;", ":;").AnyOfDelims(), ElementsAre("abc", "", "")); + EXPECT_THAT(oops::Split(":abc", ":;").AnyOfDelims(), ElementsAre("", "abc")); + EXPECT_THAT(oops::Split(";abc", ":;").AnyOfDelims(), ElementsAre("", "abc")); + EXPECT_THAT(oops::Split(":;abc", ":;").AnyOfDelims(), ElementsAre("", "", "abc")); + EXPECT_THAT(oops::Split("abc:def", ":;").AnyOfDelims(), ElementsAre("abc", "def")); + EXPECT_THAT(oops::Split("abc;def", ":;").AnyOfDelims(), ElementsAre("abc", "def")); + EXPECT_THAT(oops::Split("abc:;def", ":;").AnyOfDelims(), ElementsAre("abc", "", "def")); + EXPECT_THAT(oops::Split("abc:def;ghi", ":;").AnyOfDelims(), ElementsAre("abc", "def", "ghi")); +} + +TEST(CommonStr, SplitNone) { + // 跟随std::views::split行为,做逐字符分割 + EXPECT_THAT(oops::Split("", ""), ElementsAre("")); + EXPECT_THAT(oops::Split("a", ""), ElementsAre("a")); + EXPECT_THAT(oops::Split("ab", ""), ElementsAre("a", "b")); + EXPECT_THAT(oops::Split("abc", ""), ElementsAre("a", "b", "c")); +} + +TEST(CommonStr, SplitAnyOfNone) { + EXPECT_THAT(oops::Split("", "").AnyOfDelims(), ElementsAre("")); + EXPECT_THAT(oops::Split("a", "").AnyOfDelims(), ElementsAre("a")); + EXPECT_THAT(oops::Split("ab", "").AnyOfDelims(), ElementsAre("ab")); + EXPECT_THAT(oops::Split("abc", "").AnyOfDelims(), ElementsAre("abc")); +} + +TEST(CommonStr, SplitNotSkipEmpty) { + EXPECT_THAT(oops::Split("").SkipEmpty(false), ElementsAre("")); + EXPECT_THAT(oops::Split(" ").SkipEmpty(false), ElementsAre("", "")); + EXPECT_THAT(oops::Split(" ").SkipEmpty(false), ElementsAre("", "", "")); + EXPECT_THAT(oops::Split("abc").SkipEmpty(false), ElementsAre("abc")); + EXPECT_THAT(oops::Split("abc ").SkipEmpty(false), ElementsAre("abc", "")); + EXPECT_THAT(oops::Split("abc ").SkipEmpty(false), ElementsAre("abc", "", "")); + EXPECT_THAT(oops::Split(" abc").SkipEmpty(false), ElementsAre("", "abc")); + EXPECT_THAT(oops::Split(" abc").SkipEmpty(false), ElementsAre("", "", "abc")); + EXPECT_THAT(oops::Split("abc def").SkipEmpty(false), ElementsAre("abc", "def")); + EXPECT_THAT(oops::Split("abc def").SkipEmpty(false), ElementsAre("abc", "", "def")); + EXPECT_THAT(oops::Split("abc def ghi").SkipEmpty(false), ElementsAre("abc", "def", "ghi")); +} + +TEST(CommonStr, SplitDelimSkipEmpty) { + EXPECT_THAT(oops::Split("", ":").SkipEmpty(), ElementsAre()); + EXPECT_THAT(oops::Split(":", ":").SkipEmpty(), ElementsAre()); + EXPECT_THAT(oops::Split("::", ":").SkipEmpty(), ElementsAre()); + EXPECT_THAT(oops::Split("abc", ":").SkipEmpty(), ElementsAre("abc")); + EXPECT_THAT(oops::Split("abc:", ":").SkipEmpty(), ElementsAre("abc")); + EXPECT_THAT(oops::Split("abc::", ":").SkipEmpty(), ElementsAre("abc")); + EXPECT_THAT(oops::Split(":abc", ":").SkipEmpty(), ElementsAre("abc")); + EXPECT_THAT(oops::Split("::abc", ":").SkipEmpty(), ElementsAre("abc")); + EXPECT_THAT(oops::Split("abc:def", ":").SkipEmpty(), ElementsAre("abc", "def")); + EXPECT_THAT(oops::Split("abc::def", ":").SkipEmpty(), ElementsAre("abc", "def")); + EXPECT_THAT(oops::Split("abc:def:ghi", ":").SkipEmpty(), ElementsAre("abc", "def", "ghi")); +} + +TEST(CommonStr, Repeat) { using namespace oops; - EXPECT_THAT(Split("abc:defg:hi", ":"), ElementsAre("abc", "defg", "hi")); + EXPECT_EQ(Repeat("abc", 0), ""); + EXPECT_EQ(Repeat("abc", 1), "abc"); + EXPECT_EQ(Repeat("abc", 3), "abcabcabc"); + EXPECT_EQ(Repeat("", 0), ""); + EXPECT_EQ(Repeat("", 1), ""); + EXPECT_EQ(Repeat("", 3), ""); } TEST(CommonStr, StartsWith) { diff --git a/module/meta/test/test_type_list.cpp b/module/meta/test/test_type_list.cpp index 10e37fd..f85d5b3 100644 --- a/module/meta/test/test_type_list.cpp +++ b/module/meta/test/test_type_list.cpp @@ -1,8 +1,8 @@ #include "oops/type_list.h" #include -using namespace oops::meta; #define TEST_STATIC(suite, case) constexpr void TestStatic_##suite_##case () +using namespace oops::meta; namespace { class A {};