Commit bf475c74 authored by Francesco Zoffoli's avatar Francesco Zoffoli Committed by Facebook GitHub Bot

CoReturn can return implicitly convertible values

Summary:
Gmock's `Return(x)` is a valid action as long as `x` is implicitly convertible to the return type of the function.

The current implementation of `CoReturn(x)` does not support that, since `Task<T>` is not convertible to `Task<Q>` even if `Q` is convertible to `T`.

This diff changes the `CoReturn` implementatio to match the way Gmock does, allowing for the implicit conversion.

This is extremely valuable when using `Task<Expected<...>>`, as it allows to write `CoReturn(makeUnexpected(...))` instead of `CoReturn(Expected<...>(unexpected, ...))`.

It also make the translation from a mock being sync to async more straightforward: add `Task` in the mock return type and replace `Return` with `CoReturn`.

Reviewed By: lewissbaker

Differential Revision: D24046013

fbshipit-source-id: 9f166237a624600be51c00616f145ae4e1a45443
parent 22ed347d
......@@ -103,6 +103,42 @@ struct OnceForwarder {
std::atomic<bool> performed_ = false;
};
// Allow to return a value by providing a convertible value.
// This works similarly to Return(x):
// MOCK_METHOD1(Method, T(U));
// EXPECT_CALL(mock, Method(_)).WillOnce(Return(F()));
// should work as long as F is convertible to T.
template <typename T>
class CoReturnImpl {
public:
explicit CoReturnImpl(T&& value) : value_(std::move(value)) {}
template <typename Result, typename ArgumentTuple>
Result Perform(const ArgumentTuple& /* unused */) const {
return [](T value) -> Result { co_return value; }(value_);
}
private:
T value_;
};
template <typename T>
class CoReturnByMoveImpl {
public:
explicit CoReturnByMoveImpl(std::shared_ptr<OnceForwarder<T&&>> forwarder)
: forwarder_(std::move(forwarder)) {}
template <typename Result, typename ArgumentTuple>
Result Perform(const ArgumentTuple& /* unused */) const {
return [](std::shared_ptr<OnceForwarder<T&&>> forwarder) -> Result {
co_return (*forwarder)();
}(forwarder_);
}
private:
std::shared_ptr<OnceForwarder<T&&>> forwarder_;
};
} // namespace detail
// Helper functions to adapt CoRoutines enabled functions to be mocked using
......@@ -135,11 +171,9 @@ struct OnceForwarder {
// EXPECT_CALL(mock, co_foo(_))
// .WillRepeatedly(CoThrow<std::string>(std::runtime_error("error")));
template <typename T>
auto CoReturn(T&& ret) {
return detail::makeCoAction(
[ret = std::forward<T>(ret)]() -> Task<remove_cvref_t<T>> {
co_return ret;
});
auto CoReturn(T ret) {
return ::testing::MakePolymorphicAction(
detail::CoReturnImpl<T>(std::move(ret)));
}
inline auto CoReturn() {
......@@ -157,10 +191,8 @@ auto CoReturnByMove(T&& ret) {
auto ptr = std::make_shared<detail::OnceForwarder<T&&>>(std::move(ret));
return detail::makeCoAction(
[ptr = std::move(ptr)]() mutable -> Task<remove_cvref_t<T>> {
co_return (*ptr)();
});
return ::testing::MakePolymorphicAction(
detail::CoReturnByMoveImpl<T>(std::move(ptr)));
}
template <typename T, typename Ex>
......
......@@ -83,6 +83,18 @@ TEST(CoroGTestHelpers, CoReturnTest) {
EXPECT_EQ(ret, "abc");
}
TEST(CoroGTestHelpers, CoReturnWthImplicitConversionTest) {
MockFoo mock;
EXPECT_CALL(mock, getString()).WillRepeatedly(CoReturn("abc"));
auto ret = folly::coro::blockingWait(mock.getString());
EXPECT_EQ(ret, "abc");
ret = folly::coro::blockingWait(mock.getString());
EXPECT_EQ(ret, "abc");
}
TEST(CoroGTestHelpers, CoReturnByMoveTest) {
MockFoo mock;
......@@ -93,6 +105,29 @@ TEST(CoroGTestHelpers, CoReturnByMoveTest) {
EXPECT_EQ(ret, "abc");
}
TEST(CoroGTestHelpers, CoReturnByMoveWithImplicitConversionTest) {
MockFoo mock;
struct ImplicitToStringMoveOnly {
ImplicitToStringMoveOnly(const ImplicitToStringMoveOnly&) = delete;
ImplicitToStringMoveOnly& operator=(const ImplicitToStringMoveOnly&) =
delete;
ImplicitToStringMoveOnly(ImplicitToStringMoveOnly&&) = default;
ImplicitToStringMoveOnly& operator=(ImplicitToStringMoveOnly&&) = default;
operator std::string() {
return "abc";
}
};
EXPECT_CALL(mock, getString())
.WillRepeatedly(CoReturnByMove(ImplicitToStringMoveOnly{}));
auto ret = folly::coro::blockingWait(mock.getString());
EXPECT_EQ(ret, "abc");
}
TEST(CoroGTestHelpers, CoVoidReturnTypeTest) {
MockFoo mock;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment