Commit b7e2f57b authored by Lee Howes's avatar Lee Howes Committed by Facebook Github Bot

=delete Future::onError

Summary:
Replace Future::onError with Future::thenError:
 * to remove ambiguous typing
 * to ensure that the executor is not lost and the returned Future is still bound to an executor

This diff finally removes the legacy onError.

See:
https://fb.workplace.com/groups/fbcode/permalink/2002251863144976/
for details.

Reviewed By: yfeldblum, Orvid

Differential Revision: D15034702

fbshipit-source-id: 17faf37e79c0a31ad25c02542f09a8293e693899
parent e9440ced
main v2022.02.14.00 v2022.02.07.00 v2022.01.31.00 v2022.01.24.00 v2022.01.17.00 v2022.01.10.00 v2022.01.03.00 v2021.12.27.00 v2021.12.20.00 v2021.12.13.00 v2021.12.06.00 v2021.11.29.00 v2021.11.15.00 v2021.11.08.00 v2021.11.01.00 v2021.10.25.00 v2021.10.18.00 v2021.10.11.00 v2021.10.04.00 v2021.09.27.00 v2021.09.20.00 v2021.09.13.00 v2021.09.06.00 v2021.08.30.00 v2021.08.23.00 v2021.08.02.00 v2021.07.22.00 v2021.07.20.01 v2021.07.20.00 v2021.06.28.00 v2021.06.14.00 v2021.06.07.00 v2021.05.31.00 v2021.05.24.00 v2021.05.17.00 v2021.05.10.00 v2021.05.03.00 v2021.04.26.00 v2021.04.19.00 v2021.04.12.00 v2021.04.05.00 v2021.03.29.00 v2021.03.22.00 v2021.03.15.00 v2021.03.08.00 v2021.03.01.00 v2021.02.22.00 v2021.02.15.00 v2021.02.08.00 v2021.02.01.00 v2021.01.25.00 v2021.01.18.01 v2021.01.18.00 v2021.01.11.00 v2021.01.04.00 v2020.12.28.00 v2020.12.21.00 v2020.12.14.00 v2020.12.07.00 v2020.11.30.00 v2020.11.23.00 v2020.11.16.00 v2020.11.09.00 v2020.11.02.00 v2020.10.26.00 v2020.10.19.00 v2020.10.12.00 v2020.10.05.00 v2020.09.28.00 v2020.09.21.00 v2020.09.14.00 v2020.09.07.00 v2020.08.31.00 v2020.08.24.00 v2020.08.17.00 v2020.08.10.00 v2020.08.03.00 v2020.07.27.00 v2020.07.20.00 v2020.07.13.00 v2020.07.06.00 v2020.06.29.00 v2020.06.15.00 v2020.06.08.00 v2020.06.01.00 v2020.05.25.00 v2020.05.18.00 v2020.05.11.00 v2020.05.04.00 v2020.04.27.00 v2020.04.20.00 v2020.04.13.00 v2020.04.06.00 v2020.03.30.00 v2020.03.23.00 v2020.03.16.00 v2020.03.09.00 v2020.03.02.00 v2020.02.24.00 v2020.02.17.00 v2020.02.10.00 v2020.02.03.00 v2020.01.27.00 v2020.01.20.00 v2020.01.13.00 v2020.01.06.00 v2019.12.30.00 v2019.12.23.00 v2019.12.16.00 v2019.12.09.00 v2019.12.06.00 v2019.12.02.00 v2019.11.11.00 v2019.11.04.00 v2019.10.28.00 v2019.10.21.00 v2019.10.14.00 v2019.10.07.00 v2019.09.30.00 v2019.09.23.00 v2019.09.16.00 v2019.09.09.00 v2019.09.02.00 v2019.08.26.00 v2019.08.19.00 v2019.08.12.00 v2019.08.05.00 v2019.07.29.00 v2019.07.22.00 v2019.06.17.00 v2019.06.10.00 v2019.06.03.00 v2019.05.27.00 v2019.05.20.00 v2019.05.13.00 v2019.05.06.00 v2019.04.29.00
No related merge requests found
......@@ -275,10 +275,11 @@ class CrappyExecutor : public Executor {
TEST(Executor, CrappyExecutor) {
CrappyExecutor x;
bool flag = false;
auto f = folly::via(&x).onError([&](std::runtime_error& e) {
EXPECT_STREQ("bad", e.what());
flag = true;
});
auto f = folly::via(&x).thenError(
folly::tag_t<std::runtime_error>{}, [&](std::runtime_error&& e) {
EXPECT_STREQ("bad", e.what());
flag = true;
});
EXPECT_TRUE(flag);
}
......
......@@ -128,7 +128,7 @@ class FutureDAG : public std::enable_shared_from_this<FutureDAG> {
nodes[handle].promise.setTry(std::move(t));
});
})
.onError([this, handle](exception_wrapper ew) {
.thenError([this, handle](exception_wrapper ew) {
nodes[handle].promise.setException(std::move(ew));
});
}
......
......@@ -1284,53 +1284,6 @@ Future<Unit> Future<T>::then() && {
return std::move(*this).thenValue([](T&&) {});
}
// onError where the callback returns T
template <class T>
template <class F>
typename std::enable_if<
!is_invocable<F, exception_wrapper>::value &&
!futures::detail::Extract<F>::ReturnsFuture::value,
Future<T>>::type
Future<T>::onError(F&& func) && {
typedef typename futures::detail::Extract<F>::FirstArg Exn;
static_assert(
std::is_same<typename futures::detail::Extract<F>::RawReturn, T>::value,
"Return type of onError callback must be T or Future<T>");
// NOTE: Removes the executor to maintain historical behaviour.
return std::move(*this)
.thenError(
tag_t<Exn>{},
[func = std::forward<F>(func)](auto&& ex) mutable {
return std::forward<F>(func)(ex);
})
.via(&InlineExecutor::instance());
}
// onError where the callback returns Future<T>
template <class T>
template <class F>
typename std::enable_if<
!is_invocable<F, exception_wrapper>::value &&
futures::detail::Extract<F>::ReturnsFuture::value,
Future<T>>::type
Future<T>::onError(F&& func) && {
static_assert(
std::is_same<typename futures::detail::Extract<F>::Return, Future<T>>::
value,
"Return type of onError callback must be T or Future<T>");
typedef typename futures::detail::Extract<F>::FirstArg Exn;
// NOTE: Removes the executor to maintain historical behaviour.
return std::move(*this)
.thenError(
tag_t<Exn>{},
[func = std::forward<F>(func)](auto&& ex) mutable {
return std::forward<F>(func)(ex);
})
.via(&InlineExecutor::instance());
}
template <class T>
template <class F>
Future<T> Future<T>::ensure(F&& func) && {
......@@ -1351,43 +1304,6 @@ Future<T> Future<T>::onTimeout(Duration dur, F&& func, Timekeeper* tk) && {
});
}
template <class T>
template <class F>
typename std::enable_if<
is_invocable<F, exception_wrapper>::value &&
futures::detail::Extract<F>::ReturnsFuture::value,
Future<T>>::type
Future<T>::onError(F&& func) && {
static_assert(
std::is_same<typename futures::detail::Extract<F>::Return, Future<T>>::
value,
"Return type of onError callback must be T or Future<T>");
// NOTE: Removes the executor to maintain historical behaviour.
return std::move(*this)
.thenError(std::forward<F>(func))
.via(&InlineExecutor::instance());
}
// onError(exception_wrapper) that returns T
template <class T>
template <class F>
typename std::enable_if<
is_invocable<F, exception_wrapper>::value &&
!futures::detail::Extract<F>::ReturnsFuture::value,
Future<T>>::type
Future<T>::onError(F&& func) && {
static_assert(
std::is_same<typename futures::detail::Extract<F>::Return, Future<T>>::
value,
"Return type of onError callback must be T or Future<T>");
// NOTE: Removes the executor to maintain historical behaviour.
return std::move(*this)
.thenError(std::forward<F>(func))
.via(&InlineExecutor::instance());
}
template <class Func>
auto via(Executor::KeepAlive<> x, Func&& func) -> Future<
typename isFutureOrSemiFuture<decltype(std::declval<Func>()())>::Inner> {
......
......@@ -1469,7 +1469,7 @@ class Future : private futures::detail::FutureBase<T> {
!is_invocable<F, exception_wrapper>::value &&
!futures::detail::Extract<F>::ReturnsFuture::value,
Future<T>>::type
onError(F&& func) &&;
onError(F&& func) && = delete;
/// Overload of onError where the error continuation returns a Future<T>
///
......@@ -1489,7 +1489,7 @@ class Future : private futures::detail::FutureBase<T> {
!is_invocable<F, exception_wrapper>::value &&
futures::detail::Extract<F>::ReturnsFuture::value,
Future<T>>::type
onError(F&& func) &&;
onError(F&& func) && = delete;
/// Overload of onError that takes exception_wrapper and returns Future<T>
///
......@@ -1509,7 +1509,7 @@ class Future : private futures::detail::FutureBase<T> {
is_invocable<F, exception_wrapper>::value &&
futures::detail::Extract<F>::ReturnsFuture::value,
Future<T>>::type
onError(F&& func) &&;
onError(F&& func) && = delete;
/// Overload of onError that takes exception_wrapper and returns T
///
......@@ -1529,16 +1529,15 @@ class Future : private futures::detail::FutureBase<T> {
is_invocable<F, exception_wrapper>::value &&
!futures::detail::Extract<F>::ReturnsFuture::value,
Future<T>>::type
onError(F&& func) &&;
onError(F&& func) && = delete;
template <class R, class... Args>
Future<T> onError(R (&func)(Args...)) && {
return std::move(*this).onError(&func);
}
Future<T> onError(R (&func)(Args...)) && = delete;
// clang-format off
template <class F>
[[deprecated("ERROR: use rvalue-qualified fn, eg, std::move(future).onError(...)")]]
[[deprecated(
"onError loses the attached executor and is weakly typed. Please move to thenError instead.")]]
Future<T> onError(F&& func) & = delete;
/// func is like std::function<void()> and is executed unconditionally, and
......@@ -1559,7 +1558,7 @@ class Future : private futures::detail::FutureBase<T> {
Future<T> ensure(F&& func) &&;
// clang-format on
/// Like onError, but for timeouts. example:
/// Like thenError, but for timeouts. example:
///
/// Future<int> f = makeFuture<int>(42)
/// .delayed(long_time)
......
......@@ -26,7 +26,7 @@ using namespace folly;
namespace {
/***
* The basic premise is to check that the callback passed to then or onError
* The basic premise is to check that the callback passed to then or thenError
* is destructed before wait returns on the resulting future.
*
* The approach is to use callbacks where the destructor sleeps 500ms and then
......@@ -102,110 +102,123 @@ TEST_F(CallbackLifetimeTest, thenReturnsFutureThrows) {
EXPECT_EQ(1, *c);
}
TEST_F(CallbackLifetimeTest, onErrorTakesExnReturnsValueMatch) {
TEST_F(CallbackLifetimeTest, thenErrorTakesExnReturnsValueMatch) {
auto c = mkC();
via(&executor)
.thenValue(raise)
.onError([_ = mkCGuard(c)](std::exception&) {})
.thenError(folly::tag_t<std::exception>{}, [_ = mkCGuard(c)](auto&&) {})
.wait();
EXPECT_EQ(1, *c);
}
TEST_F(CallbackLifetimeTest, onErrorTakesExnReturnsValueMatchThrows) {
TEST_F(CallbackLifetimeTest, thenErrorTakesExnReturnsValueMatchThrows) {
auto c = mkC();
via(&executor)
.thenValue(raise)
.onError([_ = mkCGuard(c)](std::exception&) { raise(); })
.thenError(
folly::tag_t<std::exception>{},
[_ = mkCGuard(c)](auto&&) { raise(); })
.wait();
EXPECT_EQ(1, *c);
}
TEST_F(CallbackLifetimeTest, onErrorTakesExnReturnsValueWrong) {
TEST_F(CallbackLifetimeTest, thenErrorTakesExnReturnsValueWrong) {
auto c = mkC();
via(&executor)
.thenValue(raise)
.onError([_ = mkCGuard(c)](std::logic_error&) {})
.thenError(folly::tag_t<std::logic_error>{}, [_ = mkCGuard(c)](auto&&) {})
.wait();
EXPECT_EQ(1, *c);
}
TEST_F(CallbackLifetimeTest, onErrorTakesExnReturnsValueWrongThrows) {
TEST_F(CallbackLifetimeTest, thenErrorTakesExnReturnsValueWrongThrows) {
auto c = mkC();
via(&executor)
.thenValue(raise)
.onError([_ = mkCGuard(c)](std::logic_error&) { raise(); })
.thenError(
folly::tag_t<std::logic_error>{},
[_ = mkCGuard(c)](auto&&) { raise(); })
.wait();
EXPECT_EQ(1, *c);
}
TEST_F(CallbackLifetimeTest, onErrorTakesExnReturnsFutureMatch) {
TEST_F(CallbackLifetimeTest, thenErrorTakesExnReturnsFutureMatch) {
auto c = mkC();
via(&executor)
.thenValue(raise)
.onError([_ = mkCGuard(c)](std::exception&) { return makeFuture(); })
.thenError(
folly::tag_t<std::exception>{},
[_ = mkCGuard(c)](auto&&) { return makeFuture(); })
.wait();
EXPECT_EQ(1, *c);
}
TEST_F(CallbackLifetimeTest, onErrorTakesExnReturnsFutureMatchThrows) {
TEST_F(CallbackLifetimeTest, thenErrorTakesExnReturnsFutureMatchThrows) {
auto c = mkC();
via(&executor)
.thenValue(raise)
.onError([_ = mkCGuard(c)](std::exception&) { return raiseFut(); })
.thenError(
folly::tag_t<std::exception>{},
[_ = mkCGuard(c)](auto&&) { return raiseFut(); })
.wait();
EXPECT_EQ(1, *c);
}
TEST_F(CallbackLifetimeTest, onErrorTakesExnReturnsFutureWrong) {
TEST_F(CallbackLifetimeTest, thenErrorTakesExnReturnsFutureWrong) {
auto c = mkC();
via(&executor)
.thenValue(raise)
.onError([_ = mkCGuard(c)](std::logic_error&) { return makeFuture(); })
.thenError(
folly::tag_t<std::logic_error>{},
[_ = mkCGuard(c)](auto&&) { return makeFuture(); })
.wait();
EXPECT_EQ(1, *c);
}
TEST_F(CallbackLifetimeTest, onErrorTakesExnReturnsFutureWrongThrows) {
TEST_F(CallbackLifetimeTest, thenErrorTakesExnReturnsFutureWrongThrows) {
auto c = mkC();
via(&executor)
.thenValue(raise)
.onError([_ = mkCGuard(c)](std::logic_error&) { return raiseFut(); })
.thenError(
folly::tag_t<std::logic_error>{},
[_ = mkCGuard(c)](auto&&) { return raiseFut(); })
.wait();
EXPECT_EQ(1, *c);
}
TEST_F(CallbackLifetimeTest, onErrorTakesWrapReturnsValue) {
TEST_F(CallbackLifetimeTest, thenErrorTakesWrapReturnsValue) {
auto c = mkC();
via(&executor)
.thenValue(raise)
.onError([_ = mkCGuard(c)](exception_wrapper&&) {})
.thenError([_ = mkCGuard(c)](exception_wrapper&&) {})
.wait();
EXPECT_EQ(1, *c);
}
TEST_F(CallbackLifetimeTest, onErrorTakesWrapReturnsValueThrows) {
TEST_F(CallbackLifetimeTest, thenErrorTakesWrapReturnsValueThrows) {
auto c = mkC();
via(&executor)
.thenValue(raise)
.onError([_ = mkCGuard(c)](exception_wrapper&&) { raise(); })
.thenError([_ = mkCGuard(c)](exception_wrapper&&) { raise(); })
.wait();
EXPECT_EQ(1, *c);
}
TEST_F(CallbackLifetimeTest, onErrorTakesWrapReturnsFuture) {
TEST_F(CallbackLifetimeTest, thenErrorTakesWrapReturnsFuture) {
auto c = mkC();
via(&executor)
.thenValue(raise)
.onError([_ = mkCGuard(c)](exception_wrapper&&) { return makeFuture(); })
.thenError(
[_ = mkCGuard(c)](exception_wrapper&&) { return makeFuture(); })
.wait();
EXPECT_EQ(1, *c);
}
TEST_F(CallbackLifetimeTest, onErrorTakesWrapReturnsFutureThrows) {
TEST_F(CallbackLifetimeTest, thenErrorTakesWrapReturnsFutureThrows) {
auto c = mkC();
via(&executor)
.thenValue(raise)
.onError([_ = mkCGuard(c)](exception_wrapper&&) { return raiseFut(); })
.thenError([_ = mkCGuard(c)](exception_wrapper&&) { return raiseFut(); })
.wait();
EXPECT_EQ(1, *c);
}
......@@ -314,15 +314,6 @@ TEST(Future, hasPostconditionInvalid) {
}
namespace {
Future<int> onErrorHelperEggs(eggs_t&) {
return makeFuture(10);
}
Future<int> onErrorHelperGeneric(std::exception&) {
return makeFuture(20);
}
Future<int> onErrorHelperWrapper(folly::exception_wrapper&) {
return makeFuture(30);
}
Future<int> thenErrorHelperEggs(eggs_t&&) {
return makeFuture(10);
}
......@@ -334,7 +325,7 @@ Future<int> thenErrorHelperWrapper(folly::exception_wrapper&&) {
}
} // namespace
TEST(Future, onError) {
TEST(Future, thenError) {
bool theFlag = false;
auto flag = [&] { theFlag = true; };
#define EXPECT_FLAG() \
......@@ -351,9 +342,10 @@ TEST(Future, onError) {
// By reference
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](eggs_t& /* e */) { flag(); });
auto f =
makeFuture()
.thenValue([](auto&&) { throw eggs; })
.thenError(folly::tag_t<eggs_t>{}, [&](auto&& /* e */) { flag(); });
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
......@@ -361,7 +353,7 @@ TEST(Future, onError) {
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](eggs_t& /* e */) {
.thenError(folly::tag_t<eggs_t>{}, [&](auto&& /* e */) {
flag();
return makeFuture();
});
......@@ -371,9 +363,10 @@ TEST(Future, onError) {
// By value
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](eggs_t /* e */) { flag(); });
auto f =
makeFuture()
.thenValue([](auto&&) { throw eggs; })
.thenError(folly::tag_t<eggs_t>{}, [&](auto /* e */) { flag(); });
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
......@@ -381,7 +374,7 @@ TEST(Future, onError) {
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](eggs_t /* e */) {
.thenError(folly::tag_t<eggs_t>{}, [&](auto /* e */) {
flag();
return makeFuture();
});
......@@ -393,18 +386,21 @@ TEST(Future, onError) {
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](std::exception& /* e */) { flag(); });
.thenError(
folly::tag_t<std::exception>{},
[&](auto&& /* e */) { flag(); });
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](std::exception& /* e */) {
flag();
return makeFuture();
});
auto f =
makeFuture()
.thenValue([](auto&&) { throw eggs; })
.thenError(folly::tag_t<std::exception>{}, [&](auto&& /* e */) {
flag();
return makeFuture();
});
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
......@@ -413,7 +409,7 @@ TEST(Future, onError) {
{
auto f = makeFuture()
.thenValue([](auto&&) { throw - 1; })
.onError([&](int /* e */) { flag(); });
.thenError(folly::tag_t<int>{}, [&](int /* e */) { flag(); });
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
......@@ -421,7 +417,7 @@ TEST(Future, onError) {
{
auto f = makeFuture()
.thenValue([](auto&&) { throw - 1; })
.onError([&](int /* e */) {
.thenError(folly::tag_t<int>{}, [&](int /* e */) {
flag();
return makeFuture();
});
......@@ -433,43 +429,47 @@ TEST(Future, onError) {
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](eggs_t& /* e */) mutable { flag(); });
.thenError(
folly::tag_t<eggs_t>{},
[&](auto&& /* e */) mutable { flag(); });
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](eggs_t& /* e */) mutable {
flag();
return makeFuture();
});
auto f =
makeFuture()
.thenValue([](auto&&) { throw eggs; })
.thenError(folly::tag_t<eggs_t>{}, [&](auto&& /* e */) mutable {
flag();
return makeFuture();
});
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
// Function pointer
{
auto f = makeFuture()
.thenValue([](auto &&) -> int { throw eggs; })
.onError(onErrorHelperEggs)
.onError(onErrorHelperGeneric);
auto f =
makeFuture()
.thenValue([](auto &&) -> int { throw eggs; })
.thenError(folly::tag_t<eggs_t>{}, thenErrorHelperEggs)
.thenError(folly::tag_t<std::exception>{}, thenErrorHelperGeneric);
EXPECT_EQ(10, f.value());
}
{
auto f =
makeFuture()
.thenValue([](auto &&) -> int { throw std::runtime_error("test"); })
.onError(onErrorHelperEggs)
.onError(onErrorHelperGeneric);
.thenError(folly::tag_t<eggs_t>{}, thenErrorHelperEggs)
.thenError(folly::tag_t<std::exception>{}, thenErrorHelperGeneric);
EXPECT_EQ(20, f.value());
}
{
auto f =
makeFuture()
.thenValue([](auto &&) -> int { throw std::runtime_error("test"); })
.onError(onErrorHelperEggs);
.thenError(folly::tag_t<eggs_t>{}, thenErrorHelperEggs);
EXPECT_THROW(f.value(), std::runtime_error);
}
{
......@@ -499,7 +499,7 @@ TEST(Future, onError) {
{
auto f = makeFuture()
.thenValue([](auto&&) { return 42; })
.onError([&](eggs_t& /* e */) {
.thenError(folly::tag_t<eggs_t>{}, [&](auto&& /* e */) {
flag();
return -1;
});
......@@ -510,7 +510,7 @@ TEST(Future, onError) {
{
auto f = makeFuture()
.thenValue([](auto&&) { return 42; })
.onError([&](eggs_t& /* e */) {
.thenError(folly::tag_t<eggs_t>{}, [&](auto&& /* e */) {
flag();
return makeFuture<int>(-1);
});
......@@ -522,305 +522,21 @@ TEST(Future, onError) {
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](std::runtime_error& /* e */) { flag(); });
.thenError(
folly::tag_t<std::runtime_error>{},
[&](auto&& /* e */) { flag(); });
EXPECT_NO_FLAG();
EXPECT_THROW(f.value(), eggs_t);
}
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](std::runtime_error& /* e */) {
flag();
return makeFuture();
});
EXPECT_NO_FLAG();
EXPECT_THROW(f.value(), eggs_t);
}
// Returned value propagates
{
auto f = makeFuture()
.thenValue([](auto &&) -> int { throw eggs; })
.onError([&](eggs_t& /* e */) { return 42; });
EXPECT_EQ(42, f.value());
}
// Returned future propagates
{
auto f = makeFuture()
.thenValue([](auto &&) -> int { throw eggs; })
.onError([&](eggs_t& /* e */) { return makeFuture<int>(42); });
EXPECT_EQ(42, f.value());
}
// Throw in callback
{
auto f = makeFuture()
.thenValue([](auto &&) -> int { throw eggs; })
.onError([&](eggs_t& e) -> int { throw e; });
EXPECT_THROW(f.value(), eggs_t);
}
{
auto f = makeFuture()
.thenValue([](auto &&) -> int { throw eggs; })
.onError([&](eggs_t& e) -> Future<int> { throw e; });
EXPECT_THROW(f.value(), eggs_t);
}
// exception_wrapper, return Future<T>
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](exception_wrapper /* e */) {
flag();
return makeFuture();
});
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
// exception_wrapper, return Future<T> but throw
{
auto f = makeFuture()
.thenValue([](auto &&) -> int { throw eggs; })
.onError([&](exception_wrapper /* e */) -> Future<int> {
flag();
throw eggs;
});
EXPECT_FLAG();
EXPECT_THROW(f.value(), eggs_t);
}
// exception_wrapper, return T
{
auto f = makeFuture()
.thenValue([](auto &&) -> int { throw eggs; })
.onError([&](exception_wrapper /* e */) {
flag();
return -1;
});
EXPECT_FLAG();
EXPECT_EQ(-1, f.value());
}
// exception_wrapper, return T but throw
{
auto f = makeFuture()
.thenValue([](auto &&) -> int { throw eggs; })
.onError([&](exception_wrapper /* e */) -> int {
flag();
throw eggs;
});
EXPECT_FLAG();
EXPECT_THROW(f.value(), eggs_t);
}
// const exception_wrapper&
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](const exception_wrapper& /* e */) {
flag();
return makeFuture();
});
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
#undef EXPECT_FLAG
#undef EXPECT_NO_FLAG
}
TEST(Future, thenError) {
bool theFlag = false;
auto flag = [&] { theFlag = true; };
#define EXPECT_FLAG() \
do { \
EXPECT_TRUE(theFlag); \
theFlag = false; \
} while (0);
#define EXPECT_NO_FLAG() \
do { \
EXPECT_FALSE(theFlag); \
theFlag = false; \
} while (0);
// By reference
{
auto f =
makeFuture()
.thenValue([](auto&&) { throw eggs; })
.thenError(tag_t<eggs_t>{}, [&](const eggs_t& /* e */) { flag(); });
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
// By auto reference
{
auto f =
makeFuture()
.thenValue([](auto&&) { throw eggs; })
.thenError(tag_t<eggs_t>{}, [&](auto const& /* e */) { flag(); });
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](eggs_t& /* e */) {
flag();
return makeFuture();
});
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
// By value
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](eggs_t /* e */) { flag(); });
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](eggs_t /* e */) {
flag();
return makeFuture();
});
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
// Polymorphic
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](std::exception& /* e */) { flag(); });
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](std::exception& /* e */) {
flag();
return makeFuture();
});
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
// Non-exceptions
{
auto f = makeFuture()
.thenValue([](auto&&) { throw - 1; })
.onError([&](int /* e */) { flag(); });
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
{
auto f = makeFuture()
.thenValue([](auto&&) { throw - 1; })
.onError([&](int /* e */) {
flag();
return makeFuture();
});
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
// Mutable lambda
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](eggs_t& /* e */) mutable { flag(); });
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](eggs_t& /* e */) mutable {
flag();
return makeFuture();
});
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
// Function pointer
{
auto f = makeFuture()
.thenValue([](auto &&) -> int { throw eggs; })
.onError(onErrorHelperEggs)
.onError(onErrorHelperGeneric);
EXPECT_EQ(10, f.value());
}
{
auto f =
makeFuture()
.thenValue([](auto &&) -> int { throw std::runtime_error("test"); })
.onError(onErrorHelperEggs)
.onError(onErrorHelperGeneric);
EXPECT_EQ(20, f.value());
}
{
auto f =
makeFuture()
.thenValue([](auto &&) -> int { throw std::runtime_error("test"); })
.onError(onErrorHelperEggs);
EXPECT_THROW(f.value(), std::runtime_error);
}
// No throw
{
auto f = makeFuture()
.thenValue([](auto&&) { return 42; })
.onError([&](eggs_t& /* e */) {
flag();
return -1;
});
EXPECT_NO_FLAG();
EXPECT_EQ(42, f.value());
}
{
auto f = makeFuture()
.thenValue([](auto&&) { return 42; })
.onError([&](eggs_t& /* e */) {
flag();
return makeFuture<int>(-1);
});
EXPECT_NO_FLAG();
EXPECT_EQ(42, f.value());
}
// Catch different exception
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](std::runtime_error& /* e */) { flag(); });
EXPECT_NO_FLAG();
EXPECT_THROW(f.value(), eggs_t);
}
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](std::runtime_error& /* e */) {
flag();
return makeFuture();
});
.thenError(folly::tag_t<std::runtime_error>{}, [&](auto&& /* e */) {
flag();
return makeFuture();
});
EXPECT_NO_FLAG();
EXPECT_THROW(f.value(), eggs_t);
}
......@@ -829,7 +545,9 @@ TEST(Future, thenError) {
{
auto f = makeFuture()
.thenValue([](auto &&) -> int { throw eggs; })
.onError([&](eggs_t& /* e */) { return 42; });
.thenError(folly::tag_t<eggs_t>{}, [&](auto&& /* e */) {
return 42;
});
EXPECT_EQ(42, f.value());
}
......@@ -837,7 +555,9 @@ TEST(Future, thenError) {
{
auto f = makeFuture()
.thenValue([](auto &&) -> int { throw eggs; })
.onError([&](eggs_t& /* e */) { return makeFuture<int>(42); });
.thenError(folly::tag_t<eggs_t>{}, [&](auto&& /* e */) {
return makeFuture<int>(42);
});
EXPECT_EQ(42, f.value());
}
......@@ -845,14 +565,18 @@ TEST(Future, thenError) {
{
auto f = makeFuture()
.thenValue([](auto &&) -> int { throw eggs; })
.onError([&](eggs_t& e) -> int { throw e; });
.thenError(folly::tag_t<eggs_t>{}, [&](auto&& e) -> int {
throw std::move(e);
});
EXPECT_THROW(f.value(), eggs_t);
}
{
auto f = makeFuture()
.thenValue([](auto &&) -> int { throw eggs; })
.onError([&](eggs_t& e) -> Future<int> { throw e; });
.thenError(
folly::tag_t<eggs_t>{},
[&](auto&& e) -> Future<int> { throw std::move(e); });
EXPECT_THROW(f.value(), eggs_t);
}
......@@ -860,7 +584,7 @@ TEST(Future, thenError) {
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](exception_wrapper /* e */) {
.thenError([&](exception_wrapper /* e */) {
flag();
return makeFuture();
});
......@@ -872,7 +596,7 @@ TEST(Future, thenError) {
{
auto f = makeFuture()
.thenValue([](auto &&) -> int { throw eggs; })
.onError([&](exception_wrapper /* e */) -> Future<int> {
.thenError([&](exception_wrapper /* e */) -> Future<int> {
flag();
throw eggs;
});
......@@ -884,7 +608,7 @@ TEST(Future, thenError) {
{
auto f = makeFuture()
.thenValue([](auto &&) -> int { throw eggs; })
.onError([&](exception_wrapper /* e */) {
.thenError([&](exception_wrapper /* e */) {
flag();
return -1;
});
......@@ -896,7 +620,7 @@ TEST(Future, thenError) {
{
auto f = makeFuture()
.thenValue([](auto &&) -> int { throw eggs; })
.onError([&](exception_wrapper /* e */) -> int {
.thenError([&](exception_wrapper /* e */) -> int {
flag();
throw eggs;
});
......@@ -908,7 +632,7 @@ TEST(Future, thenError) {
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](const exception_wrapper& /* e */) {
.thenError([&](const exception_wrapper& /* e */) {
flag();
return makeFuture();
});
......
......@@ -52,8 +52,8 @@ class ThrowingExecutor : public folly::Executor {
TEST(SelfDestruct, throwingExecutor) {
ThrowingExecutor executor;
auto* p = new Promise<int>();
auto future =
p->getFuture().via(&executor).onError([p](ThrowingExecutorError const&) {
auto future = p->getFuture().via(&executor).thenError(
folly::tag_t<ThrowingExecutorError>{}, [p](auto const&) {
delete p;
return 456;
});
......@@ -71,7 +71,9 @@ TEST(SelfDestruct, throwingInlineExecutor) {
delete p;
throw ThrowingExecutorError("callback throws");
})
.onError([](ThrowingExecutorError const&) { return 456; });
.thenError(
folly::tag_t<ThrowingExecutorError>{},
[](auto const&) { return 456; });
p->setValue(123);
EXPECT_EQ(456, std::move(future).get());
}
......@@ -68,7 +68,9 @@ TEST(Times, success) {
auto thunk = makeThunk(ps, interrupt, ps_mutex);
auto f = folly::times(3, thunk)
.thenValue([&](auto&&) mutable { complete = true; })
.onError([&](FutureException& /* e */) { failure = true; });
.thenError(folly::tag_t<FutureException>{}, [&](auto&& /* e */) {
failure = true;
});
popAndFulfillPromise(ps, ps_mutex);
EXPECT_FALSE(complete);
......@@ -94,7 +96,9 @@ TEST(Times, failure) {
auto thunk = makeThunk(ps, interrupt, ps_mutex);
auto f = folly::times(3, thunk)
.thenValue([&](auto&&) mutable { complete = true; })
.onError([&](FutureException& /* e */) { failure = true; });
.thenError(folly::tag_t<FutureException>{}, [&](auto&& /* e */) {
failure = true;
});
popAndFulfillPromise(ps, ps_mutex);
EXPECT_FALSE(complete);
......@@ -122,7 +126,9 @@ TEST(Times, interrupt) {
auto thunk = makeThunk(ps, interrupt, ps_mutex);
auto f = folly::times(3, thunk)
.thenValue([&](auto&&) mutable { complete = true; })
.onError([&](FutureException& /* e */) { failure = true; });
.thenError(folly::tag_t<FutureException>{}, [&](auto&& /* e */) {
failure = true;
});
EXPECT_EQ(0, interrupt);
......
......@@ -70,7 +70,9 @@ TEST(WhileDo, success) {
auto thunk = makeThunk(ps, interrupt, ps_mutex);
auto f = folly::whileDo(pred, thunk)
.thenValue([&](auto&&) mutable { complete = true; })
.onError([&](FutureException& /* e */) { failure = true; });
.thenError(folly::tag_t<FutureException>{}, [&](auto&& /* e */) {
failure = true;
});
popAndFulfillPromise(ps, ps_mutex);
EXPECT_FALSE(complete);
......@@ -98,7 +100,9 @@ TEST(WhileDo, failure) {
auto thunk = makeThunk(ps, interrupt, ps_mutex);
auto f = folly::whileDo(pred, thunk)
.thenValue([&](auto&&) mutable { complete = true; })
.onError([&](FutureException& /* e */) { failure = true; });
.thenError(folly::tag_t<FutureException>{}, [&](auto&& /* e */) {
failure = true;
});
popAndFulfillPromise(ps, ps_mutex);
EXPECT_FALSE(complete);
......@@ -128,7 +132,9 @@ TEST(WhileDo, interrupt) {
auto thunk = makeThunk(ps, interrupt, ps_mutex);
auto f = folly::whileDo(pred, thunk)
.thenValue([&](auto&&) mutable { complete = true; })
.onError([&](FutureException& /* e */) { failure = true; });
.thenError(folly::tag_t<FutureException>{}, [&](auto&& /* e */) {
failure = true;
});
EXPECT_EQ(0, interrupt);
......
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