Implement testing::Rethrow to throw exceptions more easily via std::exception_ptr

We avoid overloading or specializing `testing::Throw` as this is fundamentally a different operation than throwing the object.
However, we disable the corresponding overload of `testing::Throw` to prevent likely mistakes in the usage.

Fixes: #4412
PiperOrigin-RevId: 585745469
Change-Id: I03bb585427ce51983d914e88f2bf65a13545c920
This commit is contained in:
Abseil Team 2023-11-27 13:31:59 -08:00 committed by Copybara-Service
parent b10fad38c4
commit 76bb2afb8b
2 changed files with 30 additions and 3 deletions

View File

@ -135,6 +135,7 @@
#endif #endif
#include <algorithm> #include <algorithm>
#include <exception>
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <string> #include <string>
@ -1746,6 +1747,13 @@ struct ThrowAction {
return [copy](Args...) -> R { throw copy; }; return [copy](Args...) -> R { throw copy; };
} }
}; };
struct RethrowAction {
std::exception_ptr exception;
template <typename R, typename... Args>
operator Action<R(Args...)>() const { // NOLINT
return [ex = exception](Args...) -> R { std::rethrow_exception(ex); };
}
};
#endif // GTEST_HAS_EXCEPTIONS #endif // GTEST_HAS_EXCEPTIONS
} // namespace internal } // namespace internal
@ -2062,13 +2070,23 @@ internal::ReturnPointeeAction<Ptr> ReturnPointee(Ptr pointer) {
return {pointer}; return {pointer};
} }
// Action Throw(exception) can be used in a mock function of any type
// to throw the given exception. Any copyable value can be thrown.
#if GTEST_HAS_EXCEPTIONS #if GTEST_HAS_EXCEPTIONS
// Action Throw(exception) can be used in a mock function of any type
// to throw the given exception. Any copyable value can be thrown,
// except for std::exception_ptr, which is likely a mistake if
// thrown directly.
template <typename T> template <typename T>
internal::ThrowAction<typename std::decay<T>::type> Throw(T&& exception) { typename std::enable_if<
!std::is_base_of<std::exception_ptr, typename std::decay<T>::type>::value,
internal::ThrowAction<typename std::decay<T>::type>>::type
Throw(T&& exception) {
return {std::forward<T>(exception)}; return {std::forward<T>(exception)};
} }
// Action Rethrow(exception_ptr) can be used in a mock function of any type
// to rethrow any exception_ptr. Note that the same object is thrown each time.
inline internal::RethrowAction Rethrow(std::exception_ptr exception) {
return {std::move(exception)};
}
#endif // GTEST_HAS_EXCEPTIONS #endif // GTEST_HAS_EXCEPTIONS
namespace internal { namespace internal {

View File

@ -187,6 +187,7 @@ using testing::SetErrnoAndReturn;
#if GTEST_HAS_EXCEPTIONS #if GTEST_HAS_EXCEPTIONS
using testing::Throw; using testing::Throw;
using testing::Rethrow;
#endif #endif
using testing::ContainsRegex; using testing::ContainsRegex;
@ -416,6 +417,14 @@ TEST(LinkTest, TestThrow) {
EXPECT_CALL(mock, VoidFromString(_)).WillOnce(Throw(42)); EXPECT_CALL(mock, VoidFromString(_)).WillOnce(Throw(42));
EXPECT_THROW(mock.VoidFromString(nullptr), int); EXPECT_THROW(mock.VoidFromString(nullptr), int);
} }
// Tests the linkage of the Rethrow action.
TEST(LinkTest, TestRethrow) {
Mock mock;
EXPECT_CALL(mock, VoidFromString(_))
.WillOnce(Rethrow(std::make_exception_ptr(42)));
EXPECT_THROW(mock.VoidFromString(nullptr), int);
}
#endif // GTEST_HAS_EXCEPTIONS #endif // GTEST_HAS_EXCEPTIONS
// The ACTION*() macros trigger warning C4100 (unreferenced formal // The ACTION*() macros trigger warning C4100 (unreferenced formal