Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions googlemock/test/gmock-matchers-comparisons_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::string> or Matcher<const std::string&>. 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<std::string> m1 = internal::StringView("cats");
EXPECT_TRUE(m1.Matches("cats"));
EXPECT_FALSE(m1.Matches("dogs"));

Matcher<const std::string&> 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<std::string> m1;
Matcher<const std::string&> 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<std::string> object can be implicitly
Expand Down
14 changes: 14 additions & 0 deletions googletest/include/gtest/gtest-matchers.h
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,13 @@ Matcher<const std::string&> : public internal::MatcherBase<const std::string&> {

// 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 <>
Expand All @@ -544,6 +551,13 @@ Matcher<std::string> : public internal::MatcherBase<std::string> {

// 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
Expand Down
12 changes: 12 additions & 0 deletions googletest/src/gtest-matchers.cc
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,18 @@ Matcher<internal::StringView>::Matcher(const char* s) {
Matcher<internal::StringView>::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<const std::string&>::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<std::string>::Matcher(internal::StringView s) {
*this = Eq(std::string(s));
}
#endif // GTEST_INTERNAL_HAS_STRING_VIEW

} // namespace testing