diff --git a/googlemock/include/gmock/gmock-matchers.h b/googlemock/include/gmock/gmock-matchers.h index 2f49371a6..f9ac9a28b 100644 --- a/googlemock/include/gmock/gmock-matchers.h +++ b/googlemock/include/gmock/gmock-matchers.h @@ -905,16 +905,36 @@ bool CaseInsensitiveStringEquals(const StringType& s1, const StringType& s2) { return CaseInsensitiveStringEquals(s1.substr(i1 + 1), s2.substr(i2 + 1)); } +// Case insensitive comparison support based on the CharT + +template +struct ci_matcher_supported : std::false_type {}; + +template<> +struct ci_matcher_supported : std::true_type {}; + +#if GTEST_HAS_STD_WSTRING +template<> +struct ci_matcher_supported : std::true_type {}; +#endif // GTEST_HAS_STD_WSTRING + // String matchers. // Implements equality-based string matchers like StrEq, StrCaseNe, and etc. -template +// StrCaseEq and StrCaseNe are only supported for char and wchar_t strings. +template + >, + int> = 0 + > class [[nodiscard]] StrEqualityMatcher { public: - StrEqualityMatcher(StringType str, bool expect_eq, bool case_sensitive) + StrEqualityMatcher(StringType str, bool expect_eq) : string_(std::move(str)), - expect_eq_(expect_eq), - case_sensitive_(case_sensitive) {} + expect_eq_(expect_eq) {} #if GTEST_INTERNAL_HAS_STRING_VIEW bool MatchAndExplain(const internal::StringView& s, @@ -947,9 +967,14 @@ class [[nodiscard]] StrEqualityMatcher { bool MatchAndExplain(const MatcheeStringType& s, MatchResultListener* /* listener */) const { const StringType s2(s); - const bool eq = case_sensitive_ ? s2 == string_ - : CaseInsensitiveStringEquals(s2, string_); - return expect_eq_ == eq; + if constexpr(ci_matcher_supported::value) { + const bool eq = CaseSensitive::value ? + s2 == string_ : CaseInsensitiveStringEquals(s2, string_); + return expect_eq_ == eq; + } else { + const bool eq = s2 == string_; + return expect_eq_ == eq; + } } void DescribeTo(::std::ostream* os) const { @@ -964,7 +989,7 @@ class [[nodiscard]] StrEqualityMatcher { void DescribeToHelper(bool expect_eq, ::std::ostream* os) const { *os << (expect_eq ? "is " : "isn't "); *os << "equal to "; - if (!case_sensitive_) { + if constexpr(!CaseSensitive::value) { *os << "(ignoring case) "; } UniversalPrint(string_, os); @@ -972,7 +997,6 @@ class [[nodiscard]] StrEqualityMatcher { const StringType string_; const bool expect_eq_; - const bool case_sensitive_; }; // Implements the polymorphic HasSubstr(substring) matcher, which @@ -4780,117 +4804,71 @@ internal::ResultOfMatcher ResultOf( // String matchers. // Matches a string equal to str. -template -PolymorphicMatcher> StrEq( +template > +PolymorphicMatcher>> StrEq( const internal::StringLike& str) { return MakePolymorphicMatcher( - internal::StrEqualityMatcher(std::string(str), true, true)); + internal::StrEqualityMatcher>(std::basic_string(str), true)); } // Matches a string not equal to str. -template -PolymorphicMatcher> StrNe( +template > +PolymorphicMatcher>> StrNe( const internal::StringLike& str) { return MakePolymorphicMatcher( - internal::StrEqualityMatcher(std::string(str), false, true)); + internal::StrEqualityMatcher>(std::basic_string(str), false)); } // Matches a string equal to str, ignoring case. -template -PolymorphicMatcher> StrCaseEq( +template > +PolymorphicMatcher, std::false_type>> StrCaseEq( const internal::StringLike& str) { return MakePolymorphicMatcher( - internal::StrEqualityMatcher(std::string(str), true, false)); + internal::StrEqualityMatcher, std::false_type>( + std::basic_string(str), true)); } // Matches a string not equal to str, ignoring case. -template -PolymorphicMatcher> StrCaseNe( +template > +PolymorphicMatcher, std::false_type>> StrCaseNe( const internal::StringLike& str) { - return MakePolymorphicMatcher(internal::StrEqualityMatcher( - std::string(str), false, false)); + return MakePolymorphicMatcher( + internal::StrEqualityMatcher, std::false_type>( + std::basic_string(str), false)); } // Creates a matcher that matches any string, std::string, or C string // that contains the given substring. -template -PolymorphicMatcher> HasSubstr( +template > +PolymorphicMatcher>> HasSubstr( const internal::StringLike& substring) { return MakePolymorphicMatcher( - internal::HasSubstrMatcher(std::string(substring))); + internal::HasSubstrMatcher>(std::basic_string(substring))); } // Matches a string that starts with 'prefix' (case-sensitive). -template -PolymorphicMatcher> StartsWith( +template > +PolymorphicMatcher>> StartsWith( const internal::StringLike& prefix) { return MakePolymorphicMatcher( - internal::StartsWithMatcher(std::string(prefix))); + internal::StartsWithMatcher>(std::basic_string(prefix))); } // Matches a string that ends with 'suffix' (case-sensitive). -template -PolymorphicMatcher> EndsWith( +template > +PolymorphicMatcher>> EndsWith( const internal::StringLike& suffix) { return MakePolymorphicMatcher( - internal::EndsWithMatcher(std::string(suffix))); + internal::EndsWithMatcher>(std::basic_string(suffix))); } -#if GTEST_HAS_STD_WSTRING -// Wide string matchers. - -// Matches a string equal to str. -inline PolymorphicMatcher> StrEq( - const std::wstring& str) { - return MakePolymorphicMatcher( - internal::StrEqualityMatcher(str, true, true)); -} - -// Matches a string not equal to str. -inline PolymorphicMatcher> StrNe( - const std::wstring& str) { - return MakePolymorphicMatcher( - internal::StrEqualityMatcher(str, false, true)); -} - -// Matches a string equal to str, ignoring case. -inline PolymorphicMatcher> StrCaseEq( - const std::wstring& str) { - return MakePolymorphicMatcher( - internal::StrEqualityMatcher(str, true, false)); -} - -// Matches a string not equal to str, ignoring case. -inline PolymorphicMatcher> StrCaseNe( - const std::wstring& str) { - return MakePolymorphicMatcher( - internal::StrEqualityMatcher(str, false, false)); -} - -// Creates a matcher that matches any ::wstring, std::wstring, or C wide string -// that contains the given substring. -inline PolymorphicMatcher> HasSubstr( - const std::wstring& substring) { - return MakePolymorphicMatcher( - internal::HasSubstrMatcher(substring)); -} - -// Matches a string that starts with 'prefix' (case-sensitive). -inline PolymorphicMatcher> StartsWith( - const std::wstring& prefix) { - return MakePolymorphicMatcher( - internal::StartsWithMatcher(prefix)); -} - -// Matches a string that ends with 'suffix' (case-sensitive). -inline PolymorphicMatcher> EndsWith( - const std::wstring& suffix) { - return MakePolymorphicMatcher( - internal::EndsWithMatcher(suffix)); -} - -#endif // GTEST_HAS_STD_WSTRING - // Creates a polymorphic matcher that matches a 2-tuple where the // first field == the second field. inline internal::Eq2Matcher Eq() { return internal::Eq2Matcher(); } diff --git a/googlemock/test/gmock-matchers-comparisons_test.cc b/googlemock/test/gmock-matchers-comparisons_test.cc index 3a21a9a7e..d7888cb62 100644 --- a/googlemock/test/gmock-matchers-comparisons_test.cc +++ b/googlemock/test/gmock-matchers-comparisons_test.cc @@ -1270,6 +1270,10 @@ TEST(StrEqTest, MatchesEqualString) { EXPECT_TRUE(m2.Matches("Hello")); EXPECT_FALSE(m2.Matches("Hi")); + Matcher m4 = StrEq("Hello"); + EXPECT_TRUE(m4.Matches("Hello")); + EXPECT_FALSE(m4.Matches("Hi")); + #if GTEST_INTERNAL_HAS_STRING_VIEW Matcher m3 = StrEq(internal::StringView("Hello")); @@ -1282,6 +1286,10 @@ TEST(StrEqTest, MatchesEqualString) { EXPECT_TRUE(m_empty.Matches(internal::StringView())); EXPECT_FALSE(m_empty.Matches(internal::StringView("hello"))); #endif // GTEST_INTERNAL_HAS_STRING_VIEW + + Matcher m5 = StrEq(std::string_view("Hello")); + EXPECT_TRUE(m5.Matches("Hello")); + EXPECT_FALSE(m5.Matches("Hi")); } TEST(StrEqTest, CanDescribeSelf) { @@ -1298,6 +1306,115 @@ TEST(StrEqTest, CanDescribeSelf) { EXPECT_EQ("is equal to \"\\012\\045\\0\\08\\0\\0\"", Describe(m3)); } +TEST(StrEqTest, AllowsWideChars) { + Matcher mu16c = StrEq(u"Hello"); + EXPECT_TRUE(mu16c.Matches(u"Hello")); + EXPECT_FALSE(mu16c.Matches(u"hello")); + EXPECT_FALSE(mu16c.Matches(nullptr)); + + Matcher mu16 = StrEq(std::u16string(u"Hello")); + EXPECT_TRUE(mu16.Matches(std::u16string(u"Hello"))); + EXPECT_FALSE(mu16.Matches(std::u16string(u"hello"))); + + Matcher mu16sv = StrEq(std::u16string_view(u"Hello")); + EXPECT_TRUE(mu16sv.Matches(std::u16string_view(u"Hello"))); + EXPECT_FALSE(mu16sv.Matches(std::u16string_view(u"hello"))); + + Matcher mu32c = StrEq(U"Hello"); + EXPECT_TRUE(mu32c.Matches(U"Hello")); + EXPECT_FALSE(mu32c.Matches(U"hello")); + EXPECT_FALSE(mu32c.Matches(nullptr)); + + Matcher mu32 = StrEq(std::u32string(U"Hello")); + EXPECT_TRUE(mu32.Matches(std::u32string(U"Hello"))); + EXPECT_FALSE(mu32.Matches(std::u32string(U"hello"))); + + Matcher mu32sv = StrEq(std::u32string_view(U"Hello")); + EXPECT_TRUE(mu32sv.Matches(std::u32string_view(U"Hello"))); + EXPECT_FALSE(mu32sv.Matches(std::u32string_view(U"hello"))); + +#ifdef __cpp_lib_char8_t + Matcher mu8c = StrEq(u8"Hello"); + EXPECT_TRUE(mu8c.Matches("Hello")); + EXPECT_FALSE(mu8c.Matches("hello")); + EXPECT_FALSE(mu8c.Matches(nullptr)); + + Matcher mu8 = StrEq(std::u8string(u8"Hello")); + EXPECT_TRUE(mu8.Matches(std::u8string("Hello"))); + EXPECT_FALSE(mu8.Matches(std::u8string("hello"))); + + Matcher mu8sv = StrEq(std::u8string_view(u8"Hello")); + EXPECT_TRUE(mu8sv.Matches(std::u8string_view("Hello"))); + EXPECT_FALSE(mu8sv.Matches(std::u8string_view("hello"))); +#endif // __cpp_lib_char8_t + +#if GTEST_HAS_STD_WSTRING + Matcher mwc = StrEq(L"Hello"); + EXPECT_TRUE(mwc.Matches(L"Hello")); + EXPECT_FALSE(mwc.Matches(L"hello")); + EXPECT_FALSE(mwc.Matches(nullptr)); + + Matcher mw = StrEq(std::wstring(L"Hello")); + EXPECT_TRUE(mw.Matches(std::wstring(L"Hello"))); + EXPECT_FALSE(mw.Matches(std::wstring(L"hello"))); + + Matcher mwvu = StrEq(std::wstring_view(L"Hello")); + EXPECT_TRUE(mwvu.Matches(std::wstring_view(L"Hello"))); + EXPECT_FALSE(mwvu.Matches(std::wstring_view(L"hello"))); +#endif // GTEST_HAS_STD_WSTRING +} + +TEST(StrEqTest, AllowsCustomStringLikeType) { + struct MyString { + operator std::string() const { return std::string("Hello"); } + }; + + Matcher m = StrEq(MyString{}); + EXPECT_TRUE(m.Matches("Hello")); + EXPECT_FALSE(m.Matches("hello")); + EXPECT_FALSE(m.Matches(nullptr)); + +#if GTEST_HAS_STD_WSTRING + struct MyWString { + operator std::wstring() const { return std::wstring(L"Hello"); } + }; + + Matcher mw = StrEq(MyWString{}); + EXPECT_TRUE(mw.Matches(L"Hello")); + EXPECT_FALSE(mw.Matches(L"hello")); + EXPECT_FALSE(mw.Matches(nullptr)); +#endif // GTEST_HAS_STD_WSTRING + + struct MyU16String { + operator std::u16string() const { return std::u16string(u"Hello"); } + }; + + Matcher m16 = StrEq(MyU16String{}); + EXPECT_TRUE(m16.Matches(u"Hello")); + EXPECT_FALSE(m16.Matches(u"hello")); + EXPECT_FALSE(m16.Matches(nullptr)); + + struct MyU32String { + operator std::u32string() const { return std::u32string(U"Hello"); } + }; + + Matcher m32 = StrEq(MyU32String{}); + EXPECT_TRUE(m32.Matches(U"Hello")); + EXPECT_FALSE(m32.Matches(U"hello")); + EXPECT_FALSE(m32.Matches(nullptr)); + +#ifdef __cpp_lib_char8_t + struct MyU8String { + operator std::u8string() const { return std::u8string(u8"Hello"); } + }; + + Matcher m8 = StrEq(MyU8String{}); + EXPECT_TRUE(m8.Matches(u8"Hello")); + EXPECT_FALSE(m8.Matches(u8"hello")); + EXPECT_FALSE(m8.Matches(nullptr)); +#endif // __cpp_lib_char8_t +} + TEST(StrNeTest, MatchesUnequalString) { Matcher m = StrNe("Hello"); EXPECT_TRUE(m.Matches("")); @@ -1314,6 +1431,10 @@ TEST(StrNeTest, MatchesUnequalString) { EXPECT_TRUE(m3.Matches(internal::StringView())); EXPECT_FALSE(m3.Matches(internal::StringView("Hello"))); #endif // GTEST_INTERNAL_HAS_STRING_VIEW + + Matcher m4 = StrNe(std::string_view("Hello")); + EXPECT_TRUE(m4.Matches("hello")); + EXPECT_FALSE(m4.Matches("Hello")); } TEST(StrNeTest, CanDescribeSelf) { @@ -1321,6 +1442,64 @@ TEST(StrNeTest, CanDescribeSelf) { EXPECT_EQ("isn't equal to \"Hi\"", Describe(m)); } +TEST(StrNeTest, AllowsWideChars) { + Matcher mu16 = StrNe(u"Hello"); + EXPECT_TRUE(mu16.Matches(u"")); + EXPECT_TRUE(mu16.Matches(nullptr)); + EXPECT_FALSE(mu16.Matches(u"Hello")); + + Matcher mu16s = StrNe(std::u16string(u"Hello")); + EXPECT_TRUE(mu16s.Matches(u"")); + EXPECT_FALSE(mu16s.Matches(u"Hello")); + + Matcher mu16sv = StrNe(std::u16string_view(u"Hello")); + EXPECT_TRUE(mu16sv.Matches(u"")); + EXPECT_FALSE(mu16sv.Matches(u"Hello")); + + Matcher mu32 = StrNe(U"Hello"); + EXPECT_TRUE(mu32.Matches(U"")); + EXPECT_TRUE(mu32.Matches(nullptr)); + EXPECT_FALSE(mu32.Matches(U"Hello")); + + Matcher mu32s = StrNe(std::u32string(U"Hello")); + EXPECT_TRUE(mu32s.Matches(U"")); + EXPECT_FALSE(mu32s.Matches(U"Hello")); + + Matcher mu32sv = StrNe(std::u32string_view(U"Hello")); + EXPECT_TRUE(mu32sv.Matches(U"")); + EXPECT_FALSE(mu32sv.Matches(U"Hello")); + +#ifdef __cpp_lib_char8_t + Matcher mu8 = StrNe(u8"Hello"); + EXPECT_TRUE(mu8.Matches(u8"")); + EXPECT_TRUE(mu8.Matches(nullptr)); + EXPECT_FALSE(mu8.Matches(u8"Hello")); + + Matcher mu8s = StrNe(std::u8string(u8"Hello")); + EXPECT_TRUE(mu8s.Matches(u8"")); + EXPECT_FALSE(mu8s.Matches(u8"Hello")); + + Matcher mu8sv = StrNe(std::u8string_view(u8"Hello")); + EXPECT_TRUE(mu8sv.Matches(u8"")); + EXPECT_FALSE(mu8sv.Matches(u8"Hello")); +#endif // __cpp_lib_char8_t + +#if GTEST_HAS_STD_WSTRING + Matcher mw = StrNe(L"Hello"); + EXPECT_TRUE(mw.Matches(L"")); + EXPECT_TRUE(mw.Matches(nullptr)); + EXPECT_FALSE(mw.Matches(L"Hello")); + + Matcher mws = StrNe(std::wstring(L"Hello")); + EXPECT_TRUE(mws.Matches(L"")); + EXPECT_FALSE(mws.Matches(L"Hello")); + + Matcher mwsv = StrNe(std::wstring_view(L"Hello")); + EXPECT_TRUE(mwsv.Matches(L"")); + EXPECT_FALSE(mwsv.Matches(L"Hello")); +#endif // GTEST_HAS_STD_WSTRING +} + TEST(StrCaseEqTest, MatchesEqualStringIgnoringCase) { Matcher m = StrCaseEq(std::string("Hello")); EXPECT_TRUE(m.Matches("Hello")); @@ -1340,6 +1519,10 @@ TEST(StrCaseEqTest, MatchesEqualStringIgnoringCase) { EXPECT_FALSE(m3.Matches(internal::StringView("Hi"))); EXPECT_FALSE(m3.Matches(internal::StringView())); #endif // GTEST_INTERNAL_HAS_STRING_VIEW + + Matcher m4 = StrCaseEq(std::string_view("Hello")); + EXPECT_TRUE(m4.Matches("hello")); + EXPECT_FALSE(m4.Matches("Hi")); } TEST(StrCaseEqTest, MatchesEqualStringWith0IgnoringCase) { @@ -1372,6 +1555,23 @@ TEST(StrCaseEqTest, CanDescribeSelf) { EXPECT_EQ("is equal to (ignoring case) \"Hi\"", Describe(m)); } +TEST(StrCaseEqTest, AllowsWideChars) { +#if GTEST_HAS_STD_WSTRING + Matcher mw = StrCaseEq(L"Hello"); + EXPECT_TRUE(mw.Matches(L"hello")); + EXPECT_FALSE(mw.Matches(L"Hi")); + EXPECT_FALSE(mw.Matches(nullptr)); + + Matcher mws = StrCaseEq(std::wstring(L"Hello")); + EXPECT_TRUE(mws.Matches(L"hello")); + EXPECT_FALSE(mws.Matches(L"Hi")); + + Matcher mwsv = StrCaseEq(std::wstring_view(L"Hello")); + EXPECT_TRUE(mwsv.Matches(L"hello")); + EXPECT_FALSE(mwsv.Matches(L"Hi")); +#endif // GTEST_HAS_STD_WSTRING +} + TEST(StrCaseNeTest, MatchesUnequalStringIgnoringCase) { Matcher m = StrCaseNe("Hello"); EXPECT_TRUE(m.Matches("Hi")); @@ -1391,6 +1591,11 @@ TEST(StrCaseNeTest, MatchesUnequalStringIgnoringCase) { EXPECT_FALSE(m3.Matches(internal::StringView("Hello"))); EXPECT_FALSE(m3.Matches(internal::StringView("hello"))); #endif // GTEST_INTERNAL_HAS_STRING_VIEW + + Matcher m4 = StrCaseNe(std::string_view("Hello")); + EXPECT_TRUE(m4.Matches("Hi")); + EXPECT_FALSE(m4.Matches("Hello")); + EXPECT_FALSE(m4.Matches("hello")); } TEST(StrCaseNeTest, CanDescribeSelf) { @@ -1398,6 +1603,25 @@ TEST(StrCaseNeTest, CanDescribeSelf) { EXPECT_EQ("isn't equal to (ignoring case) \"Hi\"", Describe(m)); } +TEST(StrCaseNeTest, AllowsWideChars) { +#if GTEST_HAS_STD_WSTRING + Matcher mw = StrCaseNe(L"Hello"); + EXPECT_TRUE(mw.Matches(L"Hi")); + EXPECT_TRUE(mw.Matches(nullptr)); + EXPECT_FALSE(mw.Matches(L"hello")); + + Matcher mws = StrCaseNe(std::wstring(L"Hello")); + EXPECT_TRUE(mws.Matches(L"Hi")); + EXPECT_FALSE(mws.Matches(L"Hello")); + EXPECT_FALSE(mws.Matches(L"hello")); + + Matcher mwsv = StrCaseNe(std::wstring_view(L"Hello")); + EXPECT_TRUE(mwsv.Matches(L"Hi")); + EXPECT_FALSE(mwsv.Matches(L"Hello")); + EXPECT_FALSE(mwsv.Matches(L"hello")); +#endif // GTEST_HAS_STD_WSTRING +} + // Tests that HasSubstr() works for matching string-typed values. TEST(HasSubstrTest, WorksForStringClasses) { const Matcher m1 = HasSubstr("foo"); @@ -1452,12 +1676,82 @@ TEST(HasSubstrTest, WorksForStringViewClasses) { } #endif // GTEST_INTERNAL_HAS_STRING_VIEW +TEST(HasSubstrTest, WorksForStdStringViewClasses) { + const Matcher m1 = HasSubstr(std::string_view("foo")); + EXPECT_TRUE(m1.Matches("I love food.")); + EXPECT_FALSE(m1.Matches("tofo")); + EXPECT_FALSE(m1.Matches(std::string_view())); + + const Matcher m2 = HasSubstr(std::string_view("")); + EXPECT_TRUE(m2.Matches("foo")); + EXPECT_TRUE(m2.Matches("")); + EXPECT_TRUE(m2.Matches(std::string_view())); +} + // Tests that HasSubstr(s) describes itself properly. TEST(HasSubstrTest, CanDescribeSelf) { Matcher m = HasSubstr("foo\n\""); EXPECT_EQ("has substring \"foo\\n\\\"\"", Describe(m)); } +TEST(HasSubstrTest, AllowsWideChars) { + Matcher m16 = HasSubstr(u"foo"); + EXPECT_TRUE(m16.Matches(u"I love food")); + EXPECT_FALSE(m16.Matches(nullptr)); + EXPECT_FALSE(m16.Matches(u"hello")); + + Matcher m16s = HasSubstr(std::u16string(u"foo")); + EXPECT_TRUE(m16s.Matches(u"I love food")); + EXPECT_FALSE(m16s.Matches(u"hello")); + + Matcher m16sv = HasSubstr(std::u16string_view(u"foo")); + EXPECT_TRUE(m16sv.Matches(u"I love food")); + EXPECT_FALSE(m16sv.Matches(u"hello")); + + Matcher m32 = HasSubstr(U"foo"); + EXPECT_TRUE(m32.Matches(U"I love food")); + EXPECT_FALSE(m32.Matches(nullptr)); + EXPECT_FALSE(m32.Matches(U"hello")); + + Matcher m32s = HasSubstr(std::u32string(U"foo")); + EXPECT_TRUE(m32s.Matches(U"I love food")); + EXPECT_FALSE(m32s.Matches(U"hello")); + + Matcher m32sv = HasSubstr(std::u32string_view(U"foo")); + EXPECT_TRUE(m32sv.Matches(U"I love food")); + EXPECT_FALSE(m32sv.Matches(U"hello")); + +#ifdef __cpp_lib_char8_t + Matcher m8 = HasSubstr(u8"foo"); + EXPECT_TRUE(m8.Matches(u8"I love food")); + EXPECT_FALSE(m8.Matches(nullptr)); + EXPECT_FALSE(m8.Matches(u8"hello")); + + Matcher m8s = HasSubstr(std::u8string(u8"foo")); + EXPECT_TRUE(m8s.Matches(u8"I love food")); + EXPECT_FALSE(m8s.Matches(u8"hello")); + + Matcher m8sv = HasSubstr(std::u8string_view(u8"foo")); + EXPECT_TRUE(m8sv.Matches(u8"I love food")); + EXPECT_FALSE(m8sv.Matches(u8"hello")); +#endif // __cpp_lib_char8_t + +#if GTEST_HAS_STD_WSTRING + Matcher mw = HasSubstr(L"foo"); + EXPECT_TRUE(mw.Matches(L"I love food")); + EXPECT_FALSE(mw.Matches(nullptr)); + EXPECT_FALSE(mw.Matches(L"hello")); + + Matcher mws = HasSubstr(std::wstring(L"foo")); + EXPECT_TRUE(mws.Matches(L"I love food")); + EXPECT_FALSE(mws.Matches(L"hello")); + + Matcher mwsv = HasSubstr(std::wstring_view(L"foo")); + EXPECT_TRUE(mwsv.Matches(L"I love food")); + EXPECT_FALSE(mwsv.Matches(L"hello")); +#endif // GTEST_HAS_STD_WSTRING +} + INSTANTIATE_GTEST_MATCHER_TEST_P(KeyTest); TEST(KeyTest, CanDescribeSelf) { @@ -1856,6 +2150,13 @@ TEST(StartsWithTest, MatchesStringWithGivenPrefix) { EXPECT_TRUE(m_empty.Matches(internal::StringView(""))); EXPECT_TRUE(m_empty.Matches(internal::StringView("not empty"))); #endif // GTEST_INTERNAL_HAS_STRING_VIEW + + const Matcher m3 = StartsWith(std::string_view("Hi")); + EXPECT_TRUE(m3.Matches("Hi")); + EXPECT_TRUE(m3.Matches("Hi Hi!")); + EXPECT_TRUE(m3.Matches("High")); + EXPECT_FALSE(m3.Matches("H")); + EXPECT_FALSE(m3.Matches(" Hi")); } TEST(StartsWithTest, CanDescribeSelf) { @@ -1872,6 +2173,64 @@ TEST(StartsWithTest, WorksWithStringMatcherOnStringViewMatchee) { #endif // GTEST_INTERNAL_HAS_STRING_VIEW } +TEST(StartsWithTest, AllowsWideChars) { + const Matcher m16 = StartsWith(u"Hi"); + EXPECT_TRUE(m16.Matches(u"Hi")); + EXPECT_FALSE(m16.Matches(u"hi")); + EXPECT_FALSE(m16.Matches(nullptr)); + + const Matcher m16s = StartsWith(std::u16string(u"Hi")); + EXPECT_TRUE(m16s.Matches(u"Hi")); + EXPECT_FALSE(m16s.Matches(u"hi")); + + const Matcher m16sv = StartsWith(std::u16string_view(u"Hi")); + EXPECT_TRUE(m16sv.Matches(u"Hi")); + EXPECT_FALSE(m16sv.Matches(u"hi")); + + const Matcher m32 = StartsWith(U"Hi"); + EXPECT_TRUE(m32.Matches(U"Hi")); + EXPECT_FALSE(m32.Matches(U"hi")); + EXPECT_FALSE(m32.Matches(nullptr)); + + const Matcher m32s = StartsWith(std::u32string(U"Hi")); + EXPECT_TRUE(m32s.Matches(U"Hi")); + EXPECT_FALSE(m32s.Matches(U"hi")); + + const Matcher m32sv = StartsWith(std::u32string_view(U"Hi")); + EXPECT_TRUE(m32sv.Matches(U"Hi")); + EXPECT_FALSE(m32sv.Matches(U"hi")); + +#ifdef __cpp_lib_char8_t + const Matcher m8 = StartsWith(u8"Hi"); + EXPECT_TRUE(m8.Matches(u8"Hi")); + EXPECT_FALSE(m8.Matches(u8"hi")); + EXPECT_FALSE(m8.Matches(nullptr)); + + const Matcher m8s = StartsWith(std::u8string(u8"Hi")); + EXPECT_TRUE(m8s.Matches(u8"Hi")); + EXPECT_FALSE(m8s.Matches(u8"hi")); + + const Matcher m8sv = StartsWith(std::u8string_view(u8"Hi")); + EXPECT_TRUE(m8sv.Matches(u8"Hi")); + EXPECT_FALSE(m8sv.Matches(u8"hi")); +#endif // __cpp_lib_char8_t + +#if GTEST_HAS_STD_WSTRING + const Matcher mw = StartsWith(L"Hi"); + EXPECT_TRUE(mw.Matches(L"Hi")); + EXPECT_FALSE(mw.Matches(L"hi")); + EXPECT_FALSE(mw.Matches(nullptr)); + + const Matcher mws = StartsWith(std::wstring(L"Hi")); + EXPECT_TRUE(mws.Matches(L"Hi")); + EXPECT_FALSE(mws.Matches(L"hi")); + + const Matcher mwsv = StartsWith(std::wstring_view(L"Hi")); + EXPECT_TRUE(mwsv.Matches(L"Hi")); + EXPECT_FALSE(mwsv.Matches(L"hi")); +#endif // GTEST_HAS_STD_WSTRING +} + // Tests EndsWith(s). TEST(EndsWithTest, MatchesStringWithGivenSuffix) { @@ -1895,6 +2254,13 @@ TEST(EndsWithTest, MatchesStringWithGivenSuffix) { EXPECT_TRUE(m4.Matches(internal::StringView())); EXPECT_TRUE(m4.Matches(internal::StringView(""))); #endif // GTEST_INTERNAL_HAS_STRING_VIEW + + const Matcher m5 = EndsWith(std::string_view("Hi")); + EXPECT_TRUE(m5.Matches("Hi")); + EXPECT_TRUE(m5.Matches("Wow Hi Hi")); + EXPECT_TRUE(m5.Matches("Super Hi")); + EXPECT_FALSE(m5.Matches("i")); + EXPECT_FALSE(m5.Matches("Hi ")); } TEST(EndsWithTest, CanDescribeSelf) { @@ -1902,6 +2268,64 @@ TEST(EndsWithTest, CanDescribeSelf) { EXPECT_EQ("ends with \"Hi\"", Describe(m)); } +TEST(EndsWithTest, AllowsWideChars) { + const Matcher m16 = EndsWith(u"Hi"); + EXPECT_TRUE(m16.Matches(u"Wow Hi Hi")); + EXPECT_FALSE(m16.Matches(u"hi")); + EXPECT_FALSE(m16.Matches(nullptr)); + + const Matcher m16s = EndsWith(std::u16string(u"Hi")); + EXPECT_TRUE(m16s.Matches(u"Wow Hi Hi")); + EXPECT_FALSE(m16s.Matches(u"hi")); + + const Matcher m16sv = EndsWith(std::u16string_view(u"Hi")); + EXPECT_TRUE(m16sv.Matches(u"Wow Hi Hi")); + EXPECT_FALSE(m16sv.Matches(u"hi")); + + const Matcher m32 = EndsWith(U"Hi"); + EXPECT_TRUE(m32.Matches(U"Wow Hi Hi")); + EXPECT_FALSE(m32.Matches(U"hi")); + EXPECT_FALSE(m32.Matches(nullptr)); + + const Matcher m32s = EndsWith(std::u32string(U"Hi")); + EXPECT_TRUE(m32s.Matches(U"Wow Hi Hi")); + EXPECT_FALSE(m32s.Matches(U"hi")); + + const Matcher m32sv = EndsWith(std::u32string_view(U"Hi")); + EXPECT_TRUE(m32sv.Matches(U"Wow Hi Hi")); + EXPECT_FALSE(m32sv.Matches(U"hi")); + +#ifdef __cpp_lib_char8_t + const Matcher m8 = EndsWith(u8"Hi"); + EXPECT_TRUE(m8.Matches(u8"Wow Hi Hi")); + EXPECT_FALSE(m8.Matches(u8"hi")); + EXPECT_FALSE(m8.Matches(nullptr)); + + const Matcher m8s = EndsWith(std::u8string(u8"Hi")); + EXPECT_TRUE(m8s.Matches(u8"Wow Hi Hi")); + EXPECT_FALSE(m8s.Matches(u8"hi")); + + const Matcher m8sv = EndsWith(std::u8string_view(u8"Hi")); + EXPECT_TRUE(m8sv.Matches(u8"Wow Hi Hi")); + EXPECT_FALSE(m8sv.Matches(u8"hi")); +#endif // __cpp_lib_char8_t + +#if GTEST_HAS_STD_WSTRING + const Matcher mw = EndsWith(L"Hi"); + EXPECT_TRUE(mw.Matches(L"Wow Hi Hi")); + EXPECT_FALSE(mw.Matches(L"hi")); + EXPECT_FALSE(mw.Matches(nullptr)); + + const Matcher mws = EndsWith(std::wstring(L"Hi")); + EXPECT_TRUE(mws.Matches(L"Wow Hi Hi")); + EXPECT_FALSE(mws.Matches(L"hi")); + + const Matcher mwsv = EndsWith(std::wstring_view(L"Hi")); + EXPECT_TRUE(mwsv.Matches(L"Wow Hi Hi")); + EXPECT_FALSE(mwsv.Matches(L"hi")); +#endif // GTEST_HAS_STD_WSTRING +} + // Tests WhenBase64Unescaped. TEST(WhenBase64UnescapedTest, MatchesUnescapedBase64Strings) { diff --git a/googletest/include/gtest/gtest-matchers.h b/googletest/include/gtest/gtest-matchers.h index 6d2ab14d2..b39216282 100644 --- a/googletest/include/gtest/gtest-matchers.h +++ b/googletest/include/gtest/gtest-matchers.h @@ -815,8 +815,31 @@ class [[nodiscard]] ImplicitCastEqMatcher { StoredRhs stored_rhs_; }; -template ::value>::type> +template +struct maybe_stringish : std::bool_constant, T>> { + using type = CharT; +}; + +struct default_char_type : std::true_type +{ + using type = char; +}; + +template +using get_char_type_t = typename std::disjunction< + maybe_stringish, +#if GTEST_HAS_STD_WSTRING + maybe_stringish, +#endif // GTEST_HAS_STD_WSTRING + maybe_stringish, + maybe_stringish, +#ifdef __cpp_lib_char8_t + maybe_stringish, +#endif // __cpp_lib_char8_t + default_char_type // we will default to char anyway + >::type; + +template>, T>>> using StringLike = T; // Implements polymorphic matchers MatchesRegex(regex) and