From fef8e5e23b5755b2871e835dd345eb2d48fe8fbe Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Fri, 24 Jul 2026 10:02:48 -0700 Subject: [PATCH] Make string matchers work with std::wstring_view Fixes: #4912 PiperOrigin-RevId: 953423131 Change-Id: Id288c01c9d1bd9c92ce78b89ec3e8587f4ffc2a7 --- googlemock/include/gmock/gmock-matchers.h | 106 +++++------------- .../test/gmock-matchers-comparisons_test.cc | 18 +-- googletest/include/gtest/gtest-matchers.h | 48 ++++++-- 3 files changed, 77 insertions(+), 95 deletions(-) diff --git a/googlemock/include/gmock/gmock-matchers.h b/googlemock/include/gmock/gmock-matchers.h index 603a5029f..3cd0ce069 100644 --- a/googlemock/include/gmock/gmock-matchers.h +++ b/googlemock/include/gmock/gmock-matchers.h @@ -4982,116 +4982,68 @@ internal::ResultOfMatcher ResultOf( // Matches a string equal to str. template -PolymorphicMatcher> StrEq( - const internal::StringLike& str) { +PolymorphicMatcher>> StrEq( + const T& str) { return MakePolymorphicMatcher( - internal::StrEqualityMatcher(std::string(str), true, true)); + internal::StrEqualityMatcher>( + internal::StringType(str), true, true)); } // Matches a string not equal to str. template -PolymorphicMatcher> StrNe( - const internal::StringLike& str) { +PolymorphicMatcher>> StrNe( + const T& str) { return MakePolymorphicMatcher( - internal::StrEqualityMatcher(std::string(str), false, true)); + internal::StrEqualityMatcher>( + internal::StringType(str), false, true)); } // Matches a string equal to str, ignoring case. template -PolymorphicMatcher> StrCaseEq( - const internal::StringLike& str) { +PolymorphicMatcher>> +StrCaseEq(const T& str) { return MakePolymorphicMatcher( - internal::StrEqualityMatcher(std::string(str), true, false)); + internal::StrEqualityMatcher>( + internal::StringType(str), true, false)); } // Matches a string not equal to str, ignoring case. template -PolymorphicMatcher> StrCaseNe( - const internal::StringLike& str) { - return MakePolymorphicMatcher(internal::StrEqualityMatcher( - std::string(str), false, false)); +PolymorphicMatcher>> +StrCaseNe(const T& str) { + return MakePolymorphicMatcher( + internal::StrEqualityMatcher>( + internal::StringType(str), false, false)); } // Creates a matcher that matches any string, std::string, or C string // that contains the given substring. template -PolymorphicMatcher> HasSubstr( - const internal::StringLike& substring) { +PolymorphicMatcher>> +HasSubstr(const T& substring) { return MakePolymorphicMatcher( - internal::HasSubstrMatcher(std::string(substring))); + internal::HasSubstrMatcher>( + internal::StringType(substring))); } // Matches a string that starts with 'prefix' (case-sensitive). template -PolymorphicMatcher> StartsWith( - const internal::StringLike& prefix) { +PolymorphicMatcher>> +StartsWith(const T& prefix) { return MakePolymorphicMatcher( - internal::StartsWithMatcher(std::string(prefix))); + internal::StartsWithMatcher>( + internal::StringType(prefix))); } // Matches a string that ends with 'suffix' (case-sensitive). template -PolymorphicMatcher> EndsWith( - const internal::StringLike& suffix) { +PolymorphicMatcher>> EndsWith( + const T& suffix) { return MakePolymorphicMatcher( - internal::EndsWithMatcher(std::string(suffix))); + internal::EndsWithMatcher>( + internal::StringType(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 b800f1e8b..1403b6d40 100644 --- a/googlemock/test/gmock-matchers-comparisons_test.cc +++ b/googlemock/test/gmock-matchers-comparisons_test.cc @@ -1254,22 +1254,22 @@ TEST(RefTest, ExplainsResult) { // Tests string comparison matchers. template -std::string FromStringLike(internal::StringLike str) { - return std::string(str); +internal::StringType ToString(T str) { + return internal::StringType(str); } -TEST(StringLike, TestConversions) { - EXPECT_EQ("foo", FromStringLike("foo")); - EXPECT_EQ("foo", FromStringLike(std::string("foo"))); +TEST(StringType, TestConversions) { + EXPECT_EQ("foo", ToString("foo")); + EXPECT_EQ("foo", ToString(std::string("foo"))); #if GTEST_INTERNAL_HAS_STRING_VIEW - EXPECT_EQ("foo", FromStringLike(internal::StringView("foo"))); + EXPECT_EQ("foo", ToString(internal::StringView("foo"))); #endif // GTEST_INTERNAL_HAS_STRING_VIEW // Non deducible types. - EXPECT_EQ("", FromStringLike({})); - EXPECT_EQ("foo", FromStringLike({'f', 'o', 'o'})); + EXPECT_EQ("", ToString({})); + EXPECT_EQ("foo", ToString({'f', 'o', 'o'})); const char buf[] = "foo"; - EXPECT_EQ("foo", FromStringLike({buf, buf + 3})); + EXPECT_EQ("foo", ToString({buf, buf + 3})); } TEST(StrEqTest, MatchesEqualString) { diff --git a/googletest/include/gtest/gtest-matchers.h b/googletest/include/gtest/gtest-matchers.h index d7bdd047f..805f75309 100644 --- a/googletest/include/gtest/gtest-matchers.h +++ b/googletest/include/gtest/gtest-matchers.h @@ -45,6 +45,7 @@ #include #include #include +#include #include #include "gtest/gtest-printers.h" @@ -812,9 +813,36 @@ class [[nodiscard]] ImplicitCastEqMatcher { StoredRhs stored_rhs_; }; -template >> -using StringLike = T; +// Dummy function (never defined) whose return type evaluates to std::string if +// the given type is a string-like type that can be converted to std::string, +// either directly or through an intermediate std::string_view. +template +extern std::enable_if_t || + (std::is_constructible_v && + std::is_convertible_v), + std::string> +ResolveAsString(const void* /* preferred */); + +#if GTEST_HAS_STD_WSTRING +// Same as above, but for std::wstring. In cases where both conversions are +// possible, this overload takes lower priority. +template +extern std::enable_if_t || + (std::is_constructible_v && + std::is_convertible_v), + std::wstring> +ResolveAsString(... /* fallback */); +#endif + +// Evaluates to the std::basic_string type that the given string-like type can +// be converted to. Prefers std::string over std::wstring if both are possible. +// Fails in a SFINAE-friendly way if no conversion was viable. +// +// For example, const char* and std::string_view would both evaluate to +// std::string, but std::allocator would fail to evaluate via SFINAE +// (despite std::string being constructible from it). +template +using StringType = decltype(ResolveAsString(nullptr)); // Implements polymorphic matchers MatchesRegex(regex) and // ContainsRegex(regex), which can be used as a Matcher as long as @@ -877,9 +905,10 @@ inline PolymorphicMatcher MatchesRegex( return MakePolymorphicMatcher(internal::MatchesRegexMatcher(regex, true)); } template -PolymorphicMatcher MatchesRegex( - const internal::StringLike& regex) { - return MatchesRegex(new internal::RE(std::string(regex))); +std::enable_if_t>, + PolymorphicMatcher> +MatchesRegex(const T& regex) { + return MatchesRegex(new internal::RE(internal::StringType(regex))); } // Matches a string that contains regular expression 'regex'. @@ -889,9 +918,10 @@ inline PolymorphicMatcher ContainsRegex( return MakePolymorphicMatcher(internal::MatchesRegexMatcher(regex, false)); } template -PolymorphicMatcher ContainsRegex( - const internal::StringLike& regex) { - return ContainsRegex(new internal::RE(std::string(regex))); +std::enable_if_t>, + PolymorphicMatcher> +ContainsRegex(const T& regex) { + return ContainsRegex(new internal::RE(internal::StringType(regex))); } // Creates a polymorphic matcher that matches anything equal to x.