diff --git a/googlemock/test/gmock-matchers-comparisons_test.cc b/googlemock/test/gmock-matchers-comparisons_test.cc index 3a21a9a7e..9159922fb 100644 --- a/googlemock/test/gmock-matchers-comparisons_test.cc +++ b/googlemock/test/gmock-matchers-comparisons_test.cc @@ -272,6 +272,40 @@ TEST(StringViewMatcherTest, CanBeImplicitlyConstructedFromStringView) { EXPECT_TRUE(m2.Matches("cats")); EXPECT_FALSE(m2.Matches("dogs")); } + +// Tests that a StringView object can be implicitly converted to a +// Matcher or Matcher. This is the symmetric +// counterpart of CanBeImplicitlyConstructedFromString above; without these +// constructors, a StringView falls through to a polymorphic Eq matcher that +// stores the view by value and dangles past the constructing call. +TEST(StringMatcherTest, CanBeImplicitlyConstructedFromStringView) { + Matcher m1 = internal::StringView("cats"); + EXPECT_TRUE(m1.Matches("cats")); + EXPECT_FALSE(m1.Matches("dogs")); + + Matcher m2 = internal::StringView("cats"); + EXPECT_TRUE(m2.Matches("cats")); + EXPECT_FALSE(m2.Matches("dogs")); +} + +// Tests that the matcher copies the string data and is safe to use after the +// underlying buffer the StringView was constructed from is destroyed. +TEST(StringMatcherTest, StringViewCtorIsLifetimeSafe) { + Matcher m1; + Matcher m2; + { + std::string buffer = "cats"; + m1 = internal::StringView(buffer); + m2 = internal::StringView(buffer); + // Mutate buffer and let it go out of scope to ensure the matcher does not + // alias |buffer|'s storage. + buffer = "dogs"; + } + EXPECT_TRUE(m1.Matches("cats")); + EXPECT_FALSE(m1.Matches("dogs")); + EXPECT_TRUE(m2.Matches("cats")); + EXPECT_FALSE(m2.Matches("dogs")); +} #endif // GTEST_INTERNAL_HAS_STRING_VIEW // Tests that a std::reference_wrapper object can be implicitly diff --git a/googletest/include/gtest/gtest-matchers.h b/googletest/include/gtest/gtest-matchers.h index 6d2ab14d2..a60df9aa4 100644 --- a/googletest/include/gtest/gtest-matchers.h +++ b/googletest/include/gtest/gtest-matchers.h @@ -520,6 +520,13 @@ Matcher : public internal::MatcherBase { // Allows the user to write "foo" instead of Eq("foo") sometimes. Matcher(const char* s); // NOLINT + +#if GTEST_INTERNAL_HAS_STRING_VIEW + // Allows the user to pass absl::string_views or std::string_views directly. + // Copies into a std::string so the matcher owns its data and does not alias + // a (possibly temporary) buffer in the caller. + Matcher(internal::StringView s); // NOLINT +#endif }; template <> @@ -544,6 +551,13 @@ Matcher : public internal::MatcherBase { // Allows the user to write "foo" instead of Eq("foo") sometimes. Matcher(const char* s); // NOLINT + +#if GTEST_INTERNAL_HAS_STRING_VIEW + // Allows the user to pass absl::string_views or std::string_views directly. + // Copies into a std::string so the matcher owns its data and does not alias + // a (possibly temporary) buffer in the caller. + Matcher(internal::StringView s); // NOLINT +#endif }; #if GTEST_INTERNAL_HAS_STRING_VIEW diff --git a/googletest/src/gtest-matchers.cc b/googletest/src/gtest-matchers.cc index 7e3bcc0cf..0e27f7309 100644 --- a/googletest/src/gtest-matchers.cc +++ b/googletest/src/gtest-matchers.cc @@ -93,6 +93,18 @@ Matcher::Matcher(const char* s) { Matcher::Matcher(internal::StringView s) { *this = Eq(std::string(s)); } + +// Constructs a matcher that matches a const std::string& whose value is +// equal to s. Copies into a std::string so the matcher owns its data. +Matcher::Matcher(internal::StringView s) { + *this = Eq(std::string(s)); +} + +// Constructs a matcher that matches a std::string whose value is equal to s. +// Copies into a std::string so the matcher owns its data. +Matcher::Matcher(internal::StringView s) { + *this = Eq(std::string(s)); +} #endif // GTEST_INTERNAL_HAS_STRING_VIEW } // namespace testing