Merge pull request #2904 from AmatanHead:throw-matchers
PiperOrigin-RevId: 327294137
This commit is contained in:
commit
aa4cbcdcbd
@ -4725,6 +4725,175 @@ PolymorphicMatcher<internal::variant_matcher::VariantMatcher<T> > VariantWith(
|
||||
internal::variant_matcher::VariantMatcher<T>(matcher));
|
||||
}
|
||||
|
||||
#if GTEST_HAS_EXCEPTIONS
|
||||
|
||||
// Anything inside the `internal` namespace is internal to the implementation
|
||||
// and must not be used in user code!
|
||||
namespace internal {
|
||||
|
||||
class WithWhatMatcherImpl {
|
||||
public:
|
||||
WithWhatMatcherImpl(Matcher<std::string> matcher)
|
||||
: matcher_(std::move(matcher)) {}
|
||||
|
||||
void DescribeTo(std::ostream* os) const {
|
||||
*os << "contains .what() that ";
|
||||
matcher_.DescribeTo(os);
|
||||
}
|
||||
|
||||
void DescribeNegationTo(std::ostream* os) const {
|
||||
*os << "contains .what() that does not ";
|
||||
matcher_.DescribeTo(os);
|
||||
}
|
||||
|
||||
template <typename Err>
|
||||
bool MatchAndExplain(const Err& err, MatchResultListener* listener) const {
|
||||
*listener << "which contains .what() that ";
|
||||
return matcher_.MatchAndExplain(err.what(), listener);
|
||||
}
|
||||
|
||||
private:
|
||||
const Matcher<std::string> matcher_;
|
||||
};
|
||||
|
||||
inline PolymorphicMatcher<WithWhatMatcherImpl> WithWhat(
|
||||
Matcher<std::string> m) {
|
||||
return MakePolymorphicMatcher(WithWhatMatcherImpl(std::move(m)));
|
||||
}
|
||||
|
||||
template <typename Err>
|
||||
class ExceptionMatcherImpl {
|
||||
class NeverThrown {
|
||||
public:
|
||||
const char* what() const noexcept {
|
||||
return "this exception should never be thrown";
|
||||
}
|
||||
};
|
||||
|
||||
// If the matchee raises an exception of a wrong type, we'd like to
|
||||
// catch it and print its message and type. To do that, we add an additional
|
||||
// catch clause:
|
||||
//
|
||||
// try { ... }
|
||||
// catch (const Err&) { /* an expected exception */ }
|
||||
// catch (const std::exception&) { /* exception of a wrong type */ }
|
||||
//
|
||||
// However, if the `Err` itself is `std::exception`, we'd end up with two
|
||||
// identical `catch` clauses:
|
||||
//
|
||||
// try { ... }
|
||||
// catch (const std::exception&) { /* an expected exception */ }
|
||||
// catch (const std::exception&) { /* exception of a wrong type */ }
|
||||
//
|
||||
// This can cause a warning or an error in some compilers. To resolve
|
||||
// the issue, we use a fake error type whenever `Err` is `std::exception`:
|
||||
//
|
||||
// try { ... }
|
||||
// catch (const std::exception&) { /* an expected exception */ }
|
||||
// catch (const NeverThrown&) { /* exception of a wrong type */ }
|
||||
using DefaultExceptionType = typename std::conditional<
|
||||
std::is_same<typename std::remove_cv<
|
||||
typename std::remove_reference<Err>::type>::type,
|
||||
std::exception>::value,
|
||||
const NeverThrown&, const std::exception&>::type;
|
||||
|
||||
public:
|
||||
ExceptionMatcherImpl(Matcher<const Err&> matcher)
|
||||
: matcher_(std::move(matcher)) {}
|
||||
|
||||
void DescribeTo(std::ostream* os) const {
|
||||
*os << "throws an exception which is a " << GetTypeName<Err>();
|
||||
*os << " which ";
|
||||
matcher_.DescribeTo(os);
|
||||
}
|
||||
|
||||
void DescribeNegationTo(std::ostream* os) const {
|
||||
*os << "throws an exception which is not a " << GetTypeName<Err>();
|
||||
*os << " which ";
|
||||
matcher_.DescribeNegationTo(os);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool MatchAndExplain(T&& x, MatchResultListener* listener) const {
|
||||
try {
|
||||
(void)(std::forward<T>(x)());
|
||||
} catch (const Err& err) {
|
||||
*listener << "throws an exception which is a " << GetTypeName<Err>();
|
||||
*listener << " ";
|
||||
return matcher_.MatchAndExplain(err, listener);
|
||||
} catch (DefaultExceptionType err) {
|
||||
#if GTEST_HAS_RTTI
|
||||
*listener << "throws an exception of type " << GetTypeName(typeid(err));
|
||||
*listener << " ";
|
||||
#else
|
||||
*listener << "throws an std::exception-derived type ";
|
||||
#endif
|
||||
*listener << "with description \"" << err.what() << "\"";
|
||||
return false;
|
||||
} catch (...) {
|
||||
*listener << "throws an exception of an unknown type";
|
||||
return false;
|
||||
}
|
||||
|
||||
*listener << "does not throw any exception";
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
const Matcher<const Err&> matcher_;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
// Throws()
|
||||
// Throws(exceptionMatcher)
|
||||
// ThrowsMessage(messageMatcher)
|
||||
//
|
||||
// This matcher accepts a callable and verifies that when invoked, it throws
|
||||
// an exception with the given type and properties.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// EXPECT_THAT(
|
||||
// []() { throw std::runtime_error("message"); },
|
||||
// Throws<std::runtime_error>());
|
||||
//
|
||||
// EXPECT_THAT(
|
||||
// []() { throw std::runtime_error("message"); },
|
||||
// ThrowsMessage<std::runtime_error>(HasSubstr("message")));
|
||||
//
|
||||
// EXPECT_THAT(
|
||||
// []() { throw std::runtime_error("message"); },
|
||||
// Throws<std::runtime_error>(
|
||||
// Property(&std::runtime_error::what, HasSubstr("message"))));
|
||||
|
||||
template <typename Err>
|
||||
PolymorphicMatcher<internal::ExceptionMatcherImpl<Err>> Throws() {
|
||||
return MakePolymorphicMatcher(
|
||||
internal::ExceptionMatcherImpl<Err>(A<const Err&>()));
|
||||
}
|
||||
|
||||
template <typename Err, typename ExceptionMatcher>
|
||||
PolymorphicMatcher<internal::ExceptionMatcherImpl<Err>> Throws(
|
||||
const ExceptionMatcher& exception_matcher) {
|
||||
// Using matcher cast allows users to pass a matcher of a more broad type.
|
||||
// For example user may want to pass Matcher<std::exception>
|
||||
// to Throws<std::runtime_error>, or Matcher<int64> to Throws<int32>.
|
||||
return MakePolymorphicMatcher(internal::ExceptionMatcherImpl<Err>(
|
||||
SafeMatcherCast<const Err&>(exception_matcher)));
|
||||
}
|
||||
|
||||
template <typename Err, typename MessageMatcher>
|
||||
PolymorphicMatcher<internal::ExceptionMatcherImpl<Err>> ThrowsMessage(
|
||||
MessageMatcher&& message_matcher) {
|
||||
static_assert(std::is_base_of<std::exception, Err>::value,
|
||||
"expected an std::exception-derived type");
|
||||
return Throws<Err>(internal::WithWhat(
|
||||
MatcherCast<std::string>(std::forward<MessageMatcher>(message_matcher))));
|
||||
}
|
||||
|
||||
#endif // GTEST_HAS_EXCEPTIONS
|
||||
|
||||
// These macros allow using matchers to check values in Google Test
|
||||
// tests. ASSERT_THAT(value, matcher) and EXPECT_THAT(value, matcher)
|
||||
// succeed if and only if the value matches the matcher. If the assertion
|
||||
|
@ -8117,6 +8117,190 @@ TEST(MatcherPMacroTest, WorksOnMoveOnlyType) {
|
||||
EXPECT_THAT(p, Not(UniquePointee(2)));
|
||||
}
|
||||
|
||||
#if GTEST_HAS_EXCEPTIONS
|
||||
|
||||
// std::function<void()> is used below for compatibility with older copies of
|
||||
// GCC. Normally, a raw lambda is all that is needed.
|
||||
|
||||
// Test that examples from documentation compile
|
||||
TEST(ThrowsTest, Examples) {
|
||||
EXPECT_THAT(
|
||||
std::function<void()>([]() { throw std::runtime_error("message"); }),
|
||||
Throws<std::runtime_error>());
|
||||
|
||||
EXPECT_THAT(
|
||||
std::function<void()>([]() { throw std::runtime_error("message"); }),
|
||||
ThrowsMessage<std::runtime_error>(HasSubstr("message")));
|
||||
}
|
||||
|
||||
TEST(ThrowsTest, DoesNotGenerateDuplicateCatchClauseWarning) {
|
||||
EXPECT_THAT(std::function<void()>([]() { throw std::exception(); }),
|
||||
Throws<std::exception>());
|
||||
}
|
||||
|
||||
TEST(ThrowsTest, CallableExecutedExactlyOnce) {
|
||||
size_t a = 0;
|
||||
|
||||
EXPECT_THAT(std::function<void()>([&a]() {
|
||||
a++;
|
||||
throw 10;
|
||||
}),
|
||||
Throws<int>());
|
||||
EXPECT_EQ(a, 1u);
|
||||
|
||||
EXPECT_THAT(std::function<void()>([&a]() {
|
||||
a++;
|
||||
throw std::runtime_error("message");
|
||||
}),
|
||||
Throws<std::runtime_error>());
|
||||
EXPECT_EQ(a, 2u);
|
||||
|
||||
EXPECT_THAT(std::function<void()>([&a]() {
|
||||
a++;
|
||||
throw std::runtime_error("message");
|
||||
}),
|
||||
ThrowsMessage<std::runtime_error>(HasSubstr("message")));
|
||||
EXPECT_EQ(a, 3u);
|
||||
|
||||
EXPECT_THAT(std::function<void()>([&a]() {
|
||||
a++;
|
||||
throw std::runtime_error("message");
|
||||
}),
|
||||
Throws<std::runtime_error>(
|
||||
Property(&std::runtime_error::what, HasSubstr("message"))));
|
||||
EXPECT_EQ(a, 4u);
|
||||
}
|
||||
|
||||
TEST(ThrowsTest, Describe) {
|
||||
Matcher<std::function<void()>> matcher = Throws<std::runtime_error>();
|
||||
std::stringstream ss;
|
||||
matcher.DescribeTo(&ss);
|
||||
auto explanation = ss.str();
|
||||
EXPECT_THAT(explanation, HasSubstr("std::runtime_error"));
|
||||
}
|
||||
|
||||
TEST(ThrowsTest, Success) {
|
||||
Matcher<std::function<void()>> matcher = Throws<std::runtime_error>();
|
||||
StringMatchResultListener listener;
|
||||
EXPECT_TRUE(matcher.MatchAndExplain(
|
||||
[]() { throw std::runtime_error("error message"); }, &listener));
|
||||
EXPECT_THAT(listener.str(), HasSubstr("std::runtime_error"));
|
||||
}
|
||||
|
||||
TEST(ThrowsTest, FailWrongType) {
|
||||
Matcher<std::function<void()>> matcher = Throws<std::runtime_error>();
|
||||
StringMatchResultListener listener;
|
||||
EXPECT_FALSE(matcher.MatchAndExplain(
|
||||
[]() { throw std::logic_error("error message"); }, &listener));
|
||||
EXPECT_THAT(listener.str(), HasSubstr("std::logic_error"));
|
||||
EXPECT_THAT(listener.str(), HasSubstr("\"error message\""));
|
||||
}
|
||||
|
||||
TEST(ThrowsTest, FailWrongTypeNonStd) {
|
||||
Matcher<std::function<void()>> matcher = Throws<std::runtime_error>();
|
||||
StringMatchResultListener listener;
|
||||
EXPECT_FALSE(matcher.MatchAndExplain([]() { throw 10; }, &listener));
|
||||
EXPECT_THAT(listener.str(),
|
||||
HasSubstr("throws an exception of an unknown type"));
|
||||
}
|
||||
|
||||
TEST(ThrowsTest, FailNoThrow) {
|
||||
Matcher<std::function<void()>> matcher = Throws<std::runtime_error>();
|
||||
StringMatchResultListener listener;
|
||||
EXPECT_FALSE(matcher.MatchAndExplain([]() { (void)0; }, &listener));
|
||||
EXPECT_THAT(listener.str(), HasSubstr("does not throw any exception"));
|
||||
}
|
||||
|
||||
class ThrowsPredicateTest
|
||||
: public TestWithParam<Matcher<std::function<void()>>> {};
|
||||
|
||||
TEST_P(ThrowsPredicateTest, Describe) {
|
||||
Matcher<std::function<void()>> matcher = GetParam();
|
||||
std::stringstream ss;
|
||||
matcher.DescribeTo(&ss);
|
||||
auto explanation = ss.str();
|
||||
EXPECT_THAT(explanation, HasSubstr("std::runtime_error"));
|
||||
EXPECT_THAT(explanation, HasSubstr("error message"));
|
||||
}
|
||||
|
||||
TEST_P(ThrowsPredicateTest, Success) {
|
||||
Matcher<std::function<void()>> matcher = GetParam();
|
||||
StringMatchResultListener listener;
|
||||
EXPECT_TRUE(matcher.MatchAndExplain(
|
||||
[]() { throw std::runtime_error("error message"); }, &listener));
|
||||
EXPECT_THAT(listener.str(), HasSubstr("std::runtime_error"));
|
||||
}
|
||||
|
||||
TEST_P(ThrowsPredicateTest, FailWrongType) {
|
||||
Matcher<std::function<void()>> matcher = GetParam();
|
||||
StringMatchResultListener listener;
|
||||
EXPECT_FALSE(matcher.MatchAndExplain(
|
||||
[]() { throw std::logic_error("error message"); }, &listener));
|
||||
EXPECT_THAT(listener.str(), HasSubstr("std::logic_error"));
|
||||
EXPECT_THAT(listener.str(), HasSubstr("\"error message\""));
|
||||
}
|
||||
|
||||
TEST_P(ThrowsPredicateTest, FailWrongTypeNonStd) {
|
||||
Matcher<std::function<void()>> matcher = GetParam();
|
||||
StringMatchResultListener listener;
|
||||
EXPECT_FALSE(matcher.MatchAndExplain([]() { throw 10; }, &listener));
|
||||
EXPECT_THAT(listener.str(),
|
||||
HasSubstr("throws an exception of an unknown type"));
|
||||
}
|
||||
|
||||
TEST_P(ThrowsPredicateTest, FailWrongMessage) {
|
||||
Matcher<std::function<void()>> matcher = GetParam();
|
||||
StringMatchResultListener listener;
|
||||
EXPECT_FALSE(matcher.MatchAndExplain(
|
||||
[]() { throw std::runtime_error("wrong message"); }, &listener));
|
||||
EXPECT_THAT(listener.str(), HasSubstr("std::runtime_error"));
|
||||
EXPECT_THAT(listener.str(), Not(HasSubstr("wrong message")));
|
||||
}
|
||||
|
||||
TEST_P(ThrowsPredicateTest, FailNoThrow) {
|
||||
Matcher<std::function<void()>> matcher = GetParam();
|
||||
StringMatchResultListener listener;
|
||||
EXPECT_FALSE(matcher.MatchAndExplain([]() {}, &listener));
|
||||
EXPECT_THAT(listener.str(), HasSubstr("does not throw any exception"));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
AllMessagePredicates, ThrowsPredicateTest,
|
||||
Values(Matcher<std::function<void()>>(
|
||||
ThrowsMessage<std::runtime_error>(HasSubstr("error message")))));
|
||||
|
||||
// Tests that Throws<E1>(Matcher<E2>{}) compiles even when E2 != const E1&.
|
||||
TEST(ThrowsPredicateCompilesTest, ExceptionMatcherAcceptsBroadType) {
|
||||
{
|
||||
Matcher<std::function<void()>> matcher =
|
||||
ThrowsMessage<std::runtime_error>(HasSubstr("error message"));
|
||||
EXPECT_TRUE(
|
||||
matcher.Matches([]() { throw std::runtime_error("error message"); }));
|
||||
EXPECT_FALSE(
|
||||
matcher.Matches([]() { throw std::runtime_error("wrong message"); }));
|
||||
}
|
||||
|
||||
{
|
||||
Matcher<uint64_t> inner = Eq(10);
|
||||
Matcher<std::function<void()>> matcher = Throws<uint32_t>(inner);
|
||||
EXPECT_TRUE(matcher.Matches([]() { throw(uint32_t) 10; }));
|
||||
EXPECT_FALSE(matcher.Matches([]() { throw(uint32_t) 11; }));
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that ThrowsMessage("message") is equivalent
|
||||
// to ThrowsMessage(Eq<std::string>("message")).
|
||||
TEST(ThrowsPredicateCompilesTest, MessageMatcherAcceptsNonMatcher) {
|
||||
Matcher<std::function<void()>> matcher =
|
||||
ThrowsMessage<std::runtime_error>("error message");
|
||||
EXPECT_TRUE(
|
||||
matcher.Matches([]() { throw std::runtime_error("error message"); }));
|
||||
EXPECT_FALSE(matcher.Matches(
|
||||
[]() { throw std::runtime_error("wrong error message"); }));
|
||||
}
|
||||
|
||||
#endif // GTEST_HAS_EXCEPTIONS
|
||||
|
||||
} // namespace
|
||||
} // namespace gmock_matchers_test
|
||||
} // namespace testing
|
||||
|
Loading…
Reference in New Issue
Block a user