// Copyright 2018 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include #include #include #include "examples/utils.h" #include "unittest_utils.h" namespace { class fake_context : public fit::context { public: fit::executor* executor() const override { ASSERT_CRITICAL(false); } fit::suspended_task suspend_task() override { ASSERT_CRITICAL(false); } }; template class capture_result_wrapper { public: template decltype(auto) wrap(Promise promise) { static_assert(std::is_same::value, ""); static_assert(std::is_same::value, ""); ASSERT_CRITICAL(promise); return promise.then([this](fit::result& result) { last_result = std::move(result); }); } fit::result last_result; }; struct move_only { move_only(const move_only&) = delete; move_only(move_only&&) = default; move_only& operator=(const move_only&) = delete; move_only& operator=(move_only&&) = default; }; // Just a simple test to put the promise through its paces. // Other tests go into more detail to cover the API surface. bool basics() { BEGIN_TEST; for (int i = 0; i < 5; i++) { // Make a promise that calculates half the square of a number. // Produces an error if the square is odd. auto promise = fit::make_promise([i] { // Pretend that squaring numbers is hard and takes time // to finish... return utils::sleep_for_a_little_while() .then([i](fit::result<>) { return fit::ok(i * i); }); }).then([](fit::result square) -> fit::result { if (square.value() % 2 == 0) return fit::ok(square.value() / 2); return fit::error("square is odd"); }); // Evaluate the promise. fit::result result = fit::run_single_threaded(std::move(promise)); if (i % 2 == 0) { EXPECT_TRUE(result.is_ok()); EXPECT_EQ(i * i / 2, result.value()); } else { EXPECT_TRUE(result.is_error()); EXPECT_STR_EQ("square is odd", result.error()); } } END_TEST; } // An empty promise has no continuation. // We can't do a lot with it but we can check for emptyness. bool empty_promise() { BEGIN_TEST; { fit::promise<> promise; EXPECT_FALSE(promise); } { fit::promise<> promise(nullptr); EXPECT_FALSE(promise); } { fit::function(fit::context&)> f; fit::promise<> promise(std::move(f)); EXPECT_FALSE(promise); } { std::function(fit::context&)> f; fit::promise<> promise(std::move(f)); EXPECT_FALSE(promise); } END_TEST; } bool invocation() { BEGIN_TEST; uint64_t run_count = 0; fake_context fake_context; fit::promise<> promise([&](fit::context& context) -> fit::result<> { ASSERT_CRITICAL(&context == &fake_context); if (++run_count == 2) return fit::ok(); return fit::pending(); }); EXPECT_TRUE(promise); fit::result<> result = promise(fake_context); EXPECT_EQ(1, run_count); EXPECT_EQ(fit::result_state::pending, result.state()); EXPECT_TRUE(promise); result = promise(fake_context); EXPECT_EQ(2, run_count); EXPECT_EQ(fit::result_state::ok, result.state()); EXPECT_FALSE(promise); END_TEST; } bool take_continuation() { BEGIN_TEST; uint64_t run_count = 0; fake_context fake_context; fit::promise<> promise([&](fit::context& context) -> fit::result<> { ASSERT_CRITICAL(&context == &fake_context); run_count++; return fit::pending(); }); EXPECT_TRUE(promise); fit::function(fit::context&)> f = promise.take_continuation(); EXPECT_FALSE(promise); EXPECT_EQ(0, run_count); fit::result<> result = f(fake_context); EXPECT_EQ(1, run_count); EXPECT_EQ(fit::result_state::pending, result.state()); END_TEST; } bool assignment_and_swap() { BEGIN_TEST; fake_context fake_context; fit::promise<> empty; EXPECT_FALSE(empty); uint64_t run_count = 0; fit::promise<> promise([&](fit::context& context) -> fit::result<> { run_count++; return fit::pending(); }); EXPECT_TRUE(promise); fit::promise<> x(std::move(empty)); EXPECT_FALSE(x); fit::promise<> y(std::move(promise)); EXPECT_TRUE(y); y(fake_context); EXPECT_EQ(1, run_count); x.swap(y); EXPECT_TRUE(x); EXPECT_FALSE(y); x(fake_context); EXPECT_EQ(2, run_count); x.swap(x); EXPECT_TRUE(x); x(fake_context); EXPECT_EQ(3, run_count); y.swap(y); EXPECT_FALSE(y); x = nullptr; EXPECT_FALSE(x); y = [&](fit::context& context) -> fit::result<> { run_count *= 2; return fit::pending(); }; EXPECT_TRUE(y); y(fake_context); EXPECT_EQ(6, run_count); x = std::move(y); EXPECT_TRUE(x); EXPECT_FALSE(y); x(fake_context); EXPECT_EQ(12, run_count); x = std::move(y); EXPECT_FALSE(x); END_TEST; } bool comparison_with_nullptr() { BEGIN_TEST; { fit::promise<> promise; EXPECT_TRUE(promise == nullptr); EXPECT_TRUE(nullptr == promise); EXPECT_FALSE(promise != nullptr); EXPECT_FALSE(nullptr != promise); } { fit::promise<> promise([&](fit::context& context) -> fit::result<> { return fit::pending(); }); EXPECT_FALSE(promise == nullptr); EXPECT_FALSE(nullptr == promise); EXPECT_TRUE(promise != nullptr); EXPECT_TRUE(nullptr != promise); } END_TEST; } bool make_promise() { BEGIN_TEST; fake_context fake_context; // Handler signature: void(). { uint64_t run_count = 0; auto p = fit::make_promise([&] { run_count++; }); static_assert(std::is_same::value, ""); static_assert(std::is_same::value, ""); fit::result<> result = p(fake_context); EXPECT_EQ(1, run_count); EXPECT_EQ(fit::result_state::ok, result.state()); EXPECT_FALSE(p); } // Handler signature: fit::result(). { uint64_t run_count = 0; auto p = fit::make_promise([&]() -> fit::result { run_count++; return fit::ok(42); }); static_assert(std::is_same::value, ""); static_assert(std::is_same::value, ""); fit::result result = p(fake_context); EXPECT_EQ(1, run_count); EXPECT_EQ(fit::result_state::ok, result.state()); EXPECT_EQ(42, result.value()); EXPECT_FALSE(p); } // Handler signature: fit::ok(). { uint64_t run_count = 0; auto p = fit::make_promise([&] { run_count++; return fit::ok(42); }); static_assert(std::is_same::value, ""); static_assert(std::is_same::value, ""); fit::result result = p(fake_context); EXPECT_EQ(1, run_count); EXPECT_EQ(fit::result_state::ok, result.state()); EXPECT_EQ(42, result.value()); EXPECT_FALSE(p); } // Handler signature: fit::error(). { uint64_t run_count = 0; auto p = fit::make_promise([&] { run_count++; return fit::error(42); }); static_assert(std::is_same::value, ""); static_assert(std::is_same::value, ""); fit::result result = p(fake_context); EXPECT_EQ(1, run_count); EXPECT_EQ(fit::result_state::error, result.state()); EXPECT_EQ(42, result.error()); EXPECT_FALSE(p); } // Handler signature: fit::pending(). { uint64_t run_count = 0; auto p = fit::make_promise([&] { run_count++; return fit::pending(); }); static_assert(std::is_same::value, ""); static_assert(std::is_same::value, ""); fit::result<> result = p(fake_context); EXPECT_EQ(1, run_count); EXPECT_EQ(fit::result_state::pending, result.state()); EXPECT_TRUE(p); } // Handler signature: fit::promise_impl<...>. { uint64_t run_count = 0; uint64_t run_count2 = 0; auto p = fit::make_promise([&] { run_count++; return fit::make_promise([&]() -> fit::result { if (++run_count2 == 2) return fit::ok(42); return fit::pending(); }); }); static_assert(std::is_same::value, ""); static_assert(std::is_same::value, ""); fit::result result = p(fake_context); EXPECT_EQ(1, run_count); EXPECT_EQ(1, run_count2); EXPECT_EQ(fit::result_state::pending, result.state()); EXPECT_TRUE(p); result = p(fake_context); EXPECT_EQ(1, run_count); EXPECT_EQ(2, run_count2); EXPECT_EQ(fit::result_state::ok, result.state()); EXPECT_EQ(42, result.value()); EXPECT_FALSE(p); } // Handler signature: void(context&). { uint64_t run_count = 0; auto p = fit::make_promise([&](fit::context& context) { ASSERT_CRITICAL(&context == &fake_context); run_count++; }); static_assert(std::is_same::value, ""); static_assert(std::is_same::value, ""); fit::result<> result = p(fake_context); EXPECT_EQ(1, run_count); EXPECT_EQ(fit::result_state::ok, result.state()); EXPECT_FALSE(p); } END_TEST; } // This is a bit lower level than fit::make_promise() in that there's // no automatic adaptation of the handler type. bool make_promise_with_continuation() { BEGIN_TEST; uint64_t run_count = 0; fake_context fake_context; auto p = fit::make_promise_with_continuation( [&](fit::context& context) -> fit::result { ASSERT_CRITICAL(&context == &fake_context); run_count++; return fit::ok(42); }); static_assert(std::is_same::value, ""); static_assert(std::is_same::value, ""); EXPECT_TRUE(p); fit::result result = p(fake_context); EXPECT_EQ(1, run_count); EXPECT_EQ(fit::result_state::ok, result.state()); EXPECT_EQ(42, result.value()); EXPECT_FALSE(p); END_TEST; } auto make_ok_promise(int value) { return fit::make_promise([value, count = 0]() mutable -> fit::result { ASSERT_CRITICAL(count == 0); ++count; return fit::ok(value); }); } auto make_error_promise(char error) { return fit::make_promise([error, count = 0]() mutable -> fit::result { ASSERT_CRITICAL(count == 0); ++count; return fit::error(error); }); } auto make_delayed_ok_promise(int value) { return fit::make_promise([value, count = 0]() mutable -> fit::result { ASSERT_CRITICAL(count <= 1); if (++count == 2) return fit::ok(value); return fit::pending(); }); } auto make_delayed_error_promise(char error) { return fit::make_promise([error, count = 0]() mutable -> fit::result { ASSERT_CRITICAL(count <= 1); if (++count == 2) return fit::error(error); return fit::pending(); }); } // To keep these tests manageable, we only focus on argument type adaptation // since return type adaptation logic is already covered by |make_promise()| // and by the examples. bool then_combinator() { BEGIN_TEST; fake_context fake_context; // Chaining on OK. // Handler signature: fit::result<>(fit::result). { uint64_t run_count = 0; auto p = make_delayed_ok_promise(42) .then([&](fit::result result) -> fit::result<> { ASSERT_CRITICAL(result.value() == 42); if (++run_count == 2) return fit::ok(); return fit::pending(); }); fit::result<> result = p(fake_context); EXPECT_TRUE(p); EXPECT_EQ(0, run_count); EXPECT_EQ(fit::result_state::pending, result.state()); result = p(fake_context); EXPECT_TRUE(p); EXPECT_EQ(1, run_count); EXPECT_EQ(fit::result_state::pending, result.state()); result = p(fake_context); EXPECT_FALSE(p); EXPECT_EQ(2, run_count); EXPECT_EQ(fit::result_state::ok, result.state()); } // Chaining on ERROR. // Handler signature: fit::result<>(fit::result). { uint64_t run_count = 0; auto p = make_delayed_error_promise('x') .then([&](fit::result result) -> fit::result<> { ASSERT_CRITICAL(result.error() == 'x'); if (++run_count == 2) return fit::ok(); return fit::pending(); }); fit::result<> result = p(fake_context); EXPECT_TRUE(p); EXPECT_EQ(0, run_count); EXPECT_EQ(fit::result_state::pending, result.state()); result = p(fake_context); EXPECT_TRUE(p); EXPECT_EQ(1, run_count); EXPECT_EQ(fit::result_state::pending, result.state()); result = p(fake_context); EXPECT_FALSE(p); EXPECT_EQ(2, run_count); EXPECT_EQ(fit::result_state::ok, result.state()); } // Cover all handler argument signatures, more briefly. { uint64_t run_count = 0; auto p = make_ok_promise(42) .then([&](fit::result result) -> fit::result { run_count++; return fit::ok(result.value() + 1); }) .then([&](fit::result& result) -> fit::result { run_count++; return fit::ok(result.value() + 1); }) .then([&](const fit::result& result) -> fit::result { run_count++; return fit::ok(result.value() + 1); }) .then([&](fit::context& context, fit::result result) -> fit::result { ASSERT_CRITICAL(&context == &fake_context); run_count++; return fit::ok(result.value() + 1); }) .then([&](fit::context& context, fit::result& result) -> fit::result { ASSERT_CRITICAL(&context == &fake_context); run_count++; return fit::ok(result.value() + 1); }) .then([&](fit::context& context, const fit::result& result) -> fit::result { ASSERT_CRITICAL(&context == &fake_context); run_count++; return fit::ok(result.value() + 1); }); fit::result result = p(fake_context); EXPECT_FALSE(p); EXPECT_EQ(6, run_count); EXPECT_EQ(fit::result_state::ok, result.state()); EXPECT_EQ(48, result.value()); } END_TEST; } bool and_then_combinator() { BEGIN_TEST; fake_context fake_context; // Chaining on OK. // Handler signature: fit::result<>(int). { uint64_t run_count = 0; auto p = make_delayed_ok_promise(42) .and_then([&](int value) -> fit::result { ASSERT_CRITICAL(value == 42); if (++run_count == 2) return fit::error('y'); return fit::pending(); }); fit::result result = p(fake_context); EXPECT_TRUE(p); EXPECT_EQ(0, run_count); EXPECT_EQ(fit::result_state::pending, result.state()); result = p(fake_context); EXPECT_TRUE(p); EXPECT_EQ(1, run_count); EXPECT_EQ(fit::result_state::pending, result.state()); result = p(fake_context); EXPECT_FALSE(p); EXPECT_EQ(2, run_count); EXPECT_EQ(fit::result_state::error, result.state()); EXPECT_EQ('y', result.error()); } // Chaining on ERROR. // Handler signature: fit::result<>(int). { uint64_t run_count = 0; auto p = make_delayed_error_promise('x') .and_then([&](int value) -> fit::result { run_count++; return fit::pending(); }); fit::result result = p(fake_context); EXPECT_TRUE(p); EXPECT_EQ(0, run_count); EXPECT_EQ(fit::result_state::pending, result.state()); result = p(fake_context); EXPECT_FALSE(p); EXPECT_EQ(0, run_count); EXPECT_EQ(fit::result_state::error, result.state()); EXPECT_EQ('x', result.error()); } // Cover all handler argument signatures, more briefly. { uint64_t run_count = 0; auto p = make_ok_promise(42) .and_then([&](int value) -> fit::result { run_count++; return fit::ok(value + 1); }) .and_then([&](int& value) -> fit::result { run_count++; return fit::ok(value + 1); }) .and_then([&](const int& value) -> fit::result { run_count++; return fit::ok(value + 1); }) .and_then([&](fit::context& context, int value) -> fit::result { ASSERT_CRITICAL(&context == &fake_context); run_count++; return fit::ok(value + 1); }) .and_then([&](fit::context& context, int& value) -> fit::result { ASSERT_CRITICAL(&context == &fake_context); run_count++; return fit::ok(value + 1); }) .and_then([&](fit::context& context, const int& value) -> fit::result { ASSERT_CRITICAL(&context == &fake_context); run_count++; return fit::ok(value + 1); }); fit::result result = p(fake_context); EXPECT_EQ(6, run_count); EXPECT_EQ(fit::result_state::ok, result.state()); EXPECT_EQ(48, result.value()); EXPECT_FALSE(p); } END_TEST; } bool or_else_combinator() { BEGIN_TEST; fake_context fake_context; // Chaining on OK. // Handler signature: fit::result<>(char). { uint64_t run_count = 0; auto p = make_delayed_ok_promise(42) .or_else([&](char error) -> fit::result { run_count++; return fit::pending(); }); fit::result result = p(fake_context); EXPECT_TRUE(p); EXPECT_EQ(0, run_count); EXPECT_EQ(fit::result_state::pending, result.state()); result = p(fake_context); EXPECT_FALSE(p); EXPECT_EQ(0, run_count); EXPECT_EQ(fit::result_state::ok, result.state()); EXPECT_EQ(42, result.value()); } // Chaining on ERROR. // Handler signature: fit::result<>(char). { uint64_t run_count = 0; auto p = make_delayed_error_promise('x') .or_else([&](char error) -> fit::result { ASSERT_CRITICAL(error == 'x'); if (++run_count == 2) return fit::ok(43); return fit::pending(); }); fit::result result = p(fake_context); EXPECT_TRUE(p); EXPECT_EQ(0, run_count); EXPECT_EQ(fit::result_state::pending, result.state()); result = p(fake_context); EXPECT_TRUE(p); EXPECT_EQ(1, run_count); EXPECT_EQ(fit::result_state::pending, result.state()); result = p(fake_context); EXPECT_FALSE(p); EXPECT_EQ(2, run_count); EXPECT_EQ(fit::result_state::ok, result.state()); EXPECT_EQ(43, result.value()); } // Cover all handler argument signatures, more briefly. { uint64_t run_count = 0; auto p = make_error_promise('a') .or_else([&](char error) -> fit::result { run_count++; return fit::error(error + 1); }) .or_else([&](char& error) -> fit::result { run_count++; return fit::error(error + 1); }) .or_else([&](const char& error) -> fit::result { run_count++; return fit::error(error + 1); }) .or_else([&](fit::context& context, char error) -> fit::result { ASSERT_CRITICAL(&context == &fake_context); run_count++; return fit::error(error + 1); }) .or_else([&](fit::context& context, char& error) -> fit::result { ASSERT_CRITICAL(&context == &fake_context); run_count++; return fit::error(error + 1); }) .or_else([&](fit::context& context, const char& error) -> fit::result { ASSERT_CRITICAL(&context == &fake_context); run_count++; return fit::error(error + 1); }); fit::result result = p(fake_context); EXPECT_EQ(6, run_count); EXPECT_EQ(fit::result_state::error, result.state()); EXPECT_EQ('g', result.error()); EXPECT_FALSE(p); } END_TEST; } bool inspect_combinator() { BEGIN_TEST; fake_context fake_context; // Chaining on OK. // Handler signature: void(fit::result). { uint64_t run_count = 0; auto p = make_delayed_ok_promise(42) .inspect([&](fit::result result) { ASSERT_CRITICAL(result.value() == 42); run_count++; }); fit::result result = p(fake_context); EXPECT_TRUE(p); EXPECT_EQ(0, run_count); EXPECT_EQ(fit::result_state::pending, result.state()); result = p(fake_context); EXPECT_FALSE(p); EXPECT_EQ(1, run_count); EXPECT_EQ(fit::result_state::ok, result.state()); EXPECT_EQ(42, result.value()); } // Chaining on ERROR. // Handler signature: void(fit::result). { uint64_t run_count = 0; auto p = make_delayed_error_promise('x') .inspect([&](fit::result result) { ASSERT_CRITICAL(result.error() == 'x'); run_count++; }); fit::result result = p(fake_context); EXPECT_TRUE(p); EXPECT_EQ(0, run_count); EXPECT_EQ(fit::result_state::pending, result.state()); result = p(fake_context); EXPECT_FALSE(p); EXPECT_EQ(1, run_count); EXPECT_EQ(fit::result_state::error, result.state()); EXPECT_EQ('x', result.error()); } // Cover all handler argument signatures, more briefly. { uint64_t run_count = 0; auto p = make_ok_promise(42) .inspect([&](fit::result result) { ASSERT_CRITICAL(result.value() == 42); run_count++; }) .inspect([&](fit::result& result) { ASSERT_CRITICAL(result.value() == 42); run_count++; result = fit::ok(result.value() + 1); }) .inspect([&](const fit::result& result) { ASSERT_CRITICAL(result.value() == 43); run_count++; }) .inspect([&](fit::context& context, fit::result result) { ASSERT_CRITICAL(result.value() == 43); ASSERT_CRITICAL(&context == &fake_context); run_count++; }) .inspect([&](fit::context& context, fit::result& result) { ASSERT_CRITICAL(result.value() == 43); ASSERT_CRITICAL(&context == &fake_context); run_count++; result = fit::ok(result.value() + 1); }) .inspect([&](fit::context& context, const fit::result& result) { ASSERT_CRITICAL(result.value() == 44); ASSERT_CRITICAL(&context == &fake_context); run_count++; }); fit::result result = p(fake_context); EXPECT_FALSE(p); EXPECT_EQ(6, run_count); EXPECT_EQ(fit::result_state::ok, result.state()); EXPECT_EQ(44, result.value()); } END_TEST; } bool discard_result_combinator() { BEGIN_TEST; fake_context fake_context; // Chaining on OK. { auto p = make_delayed_ok_promise(42).discard_result(); static_assert(std::is_same::value, ""); static_assert(std::is_same::value, ""); fit::result<> result = p(fake_context); EXPECT_TRUE(p); EXPECT_EQ(fit::result_state::pending, result.state()); result = p(fake_context); EXPECT_FALSE(p); EXPECT_EQ(fit::result_state::ok, result.state()); } // Chaining on ERROR. { auto p = make_delayed_error_promise('x').discard_result(); static_assert(std::is_same::value, ""); static_assert(std::is_same::value, ""); fit::result<> result = p(fake_context); EXPECT_TRUE(p); EXPECT_EQ(fit::result_state::pending, result.state()); result = p(fake_context); EXPECT_FALSE(p); EXPECT_EQ(fit::result_state::ok, result.state()); } END_TEST; } bool wrap_with_combinator() { BEGIN_TEST; fake_context fake_context; capture_result_wrapper wrapper; uint64_t successor_run_count = 0; // Apply a wrapper which steals a promise's result th auto p = make_delayed_ok_promise(42) .wrap_with(wrapper) .then([&](fit::result<>) { successor_run_count++; }); static_assert(std::is_same::value, ""); static_assert(std::is_same::value, ""); fit::result<> result = p(fake_context); EXPECT_TRUE(p); EXPECT_EQ(fit::result_state::pending, result.state()); EXPECT_EQ(fit::result_state::pending, wrapper.last_result.state()); EXPECT_EQ(0, successor_run_count); result = p(fake_context); EXPECT_FALSE(p); EXPECT_EQ(fit::result_state::ok, result.state()); EXPECT_EQ(fit::result_state::ok, wrapper.last_result.state()); EXPECT_EQ(42, wrapper.last_result.value()); EXPECT_EQ(1, successor_run_count); END_TEST; } bool box_combinator() { BEGIN_TEST; fake_context fake_context; auto p = fit::make_promise([&]() -> fit::result { return fit::ok(42); }); static_assert(!std::is_same, decltype(p)>::value, ""); auto q = p.box(); static_assert(std::is_same, decltype(q)>::value, ""); EXPECT_TRUE(q); EXPECT_FALSE(p); fit::result result = q(fake_context); EXPECT_FALSE(q); EXPECT_EQ(fit::result_state::ok, result.state()); EXPECT_EQ(42, result.value()); END_TEST; } bool join_combinator() { BEGIN_TEST; fake_context fake_context; auto p = fit::join_promises( make_ok_promise(42), make_error_promise('x').or_else([](char error) { return fit::error('y'); }), make_delayed_ok_promise(55)); EXPECT_TRUE(p); fit::result, fit::result, fit::result>> result = p(fake_context); EXPECT_TRUE(p); EXPECT_EQ(fit::result_state::pending, result.state()); result = p(fake_context); EXPECT_FALSE(p); EXPECT_EQ(fit::result_state::ok, result.state()); EXPECT_EQ(42, std::get<0>(result.value()).value()); EXPECT_EQ('y', std::get<1>(result.value()).error()); EXPECT_EQ(55, std::get<2>(result.value()).value()); END_TEST; } // Ensure that fit::promise is considered nullable so that a promise can be // directly stored as the continuation of another promise without any // additional wrappers, similar to fit::function. static_assert(fit::is_nullable>::value, ""); // Test return type adapation performed by handler invokers. // These tests verify that the necessary specializations can be produced // in all cases for handlers with various signatures. namespace handler_invoker_test { // handler returning void... static_assert(std::is_same< fit::result<>, fit::internal::result_handler_invoker< void (*)(fit::result&), fit::result>::result_type>::value, ""); static_assert(std::is_same< fit::result, fit::internal::value_handler_invoker< void (*)(int&), fit::result>::result_type>::value, ""); static_assert(std::is_same< fit::result, fit::internal::error_handler_invoker< void (*)(double&), fit::result>::result_type>::value, ""); // handler returning fit::pending_result... static_assert(std::is_same< fit::result<>, fit::internal::result_handler_invoker< fit::pending_result (*)(fit::result&), fit::result>::result_type>::value, ""); static_assert(std::is_same< fit::result, fit::internal::value_handler_invoker< fit::pending_result (*)(int&), fit::result>::result_type>::value, ""); static_assert(std::is_same< fit::result, fit::internal::error_handler_invoker< fit::pending_result (*)(double&), fit::result>::result_type>::value, ""); // handler returning fit::ok_result... static_assert(std::is_same< fit::result, fit::internal::result_handler_invoker< fit::ok_result (*)(fit::result&), fit::result>::result_type>::value, ""); static_assert(std::is_same< fit::result, fit::internal::value_handler_invoker< fit::ok_result (*)(int&), fit::result>::result_type>::value, ""); static_assert(std::is_same< fit::result, fit::internal::error_handler_invoker< fit::ok_result (*)(double&), fit::result>::result_type>::value, ""); // handler returning fit::error_result... static_assert(std::is_same< fit::result, fit::internal::result_handler_invoker< fit::error_result (*)(fit::result&), fit::result>::result_type>::value, ""); static_assert(std::is_same< fit::result, fit::internal::value_handler_invoker< fit::error_result (*)(int&), fit::result>::result_type>::value, ""); static_assert(std::is_same< fit::result, fit::internal::error_handler_invoker< fit::error_result (*)(double&), fit::result>::result_type>::value, ""); // handler returning fit::result... static_assert(std::is_same< fit::result, fit::internal::result_handler_invoker< fit::result (*)(fit::result&), fit::result>::result_type>::value, ""); static_assert(std::is_same< fit::result, fit::internal::value_handler_invoker< fit::result (*)(int&), fit::result>::result_type>::value, ""); static_assert(std::is_same< fit::result, fit::internal::error_handler_invoker< fit::result (*)(double&), fit::result>::result_type>::value, ""); // handler returning fit::promise... static_assert(std::is_same< fit::result, fit::internal::result_handler_invoker< fit::promise (*)(fit::result&), fit::result>::result_type>::value, ""); static_assert(std::is_same< fit::result, fit::internal::value_handler_invoker< fit::promise (*)(int&), fit::result>::result_type>::value, ""); static_assert(std::is_same< fit::result, fit::internal::error_handler_invoker< fit::promise (*)(double&), fit::result>::result_type>::value, ""); // handler returning lambda... auto result_continuation_lambda = [](fit::result&) -> fit::result { return fit::pending(); }; auto value_continuation_lambda = [](int&) -> fit::result { return fit::pending(); }; auto error_continuation_lambda = [](double&) -> fit::result { return fit::pending(); }; static_assert(std::is_same< fit::result, fit::internal::result_handler_invoker< decltype(result_continuation_lambda), fit::result>::result_type>::value, ""); static_assert(std::is_same< fit::result, fit::internal::value_handler_invoker< decltype(value_continuation_lambda), fit::result>::result_type>::value, ""); static_assert(std::is_same< fit::result, fit::internal::error_handler_invoker< decltype(error_continuation_lambda), fit::result>::result_type>::value, ""); } // namespace handler_invoker_test // Test predicate which is used interally to improve the quality of // compilation errors when an invalid continuation type is encountered. namespace is_continuation_test { static_assert(fit::internal::is_continuation< fit::function(fit::context&)>>::value, ""); static_assert(!fit::internal::is_continuation< fit::function>::value, ""); static_assert(!fit::internal::is_continuation< fit::function()>>::value, ""); static_assert(!fit::internal::is_continuation< void>::value, ""); auto continuation_lambda = [](fit::context&) -> fit::result<> { return fit::pending(); }; auto invalid_lambda = [] {}; static_assert(fit::internal::is_continuation< decltype(continuation_lambda)>::value, ""); static_assert(!fit::internal::is_continuation< decltype(invalid_lambda)>::value, ""); } // namespace is_continuation_test } // namespace // These are compile-time diagnostic tests. // We expect the following tests to fail at compile time and produce helpful // static assertions when enabled manually. #if 0 void diagnose_handler_with_invalid_return_type() { // Doesn't work because result isn't fit::result<>, fit::ok_result<>, // fit::error_result<>, fit::pending_result, a continuation, or void. fit::make_promise([]() -> int { return 0; }); } #endif #if 0 void diagnose_handler_with_too_few_arguments() { // Expected between 1 and 2 arguments, got 0. fit::make_promise([] {}) .then([]() {}); } #endif #if 0 void diagnose_handler_with_too_many_arguments() { // Expected between 1 and 2 arguments, got 3. fit::make_promise([] {}) .then([](fit::context&, fit::result<>, int excess) {}); } #endif #if 0 void diagnose_handler_with_invalid_context_arg() { // When there are two argument, the first must be fit::context&. fit::make_promise([] {}) .then([](fit::result<>, int excess) {}); } #endif #if 0 void diagnose_handler_with_invalid_result_arg() { // The result type must match that produced by the prior. fit::make_promise([] {}) .then([](fit::result& result) {}); } #endif #if 0 void diagnose_handler_with_invalid_move_only_result_arg() { // Move-only types must be passed by reference not by value. fit::make_promise([] { return fit::ok(move_only{}); }) .then([](fit::result result) {}); } #endif #if 0 void diagnose_handler_with_invalid_value_arg() { // The value type must match that produced by the prior. fit::make_promise([] { return fit::ok(3.2f); }) .and_then([](int value) {}); } #endif #if 0 void diagnose_handler_with_invalid_move_only_value_arg() { // The value type must match that produced by the prior. fit::make_promise([] { return fit::ok(move_only{}); }) .and_then([](move_only value) {}); } #endif #if 0 void diagnose_handler_with_invalid_error_arg() { // The error type must match that produced by the prior. fit::make_promise([] { return fit::error(3.2f); }) .or_else([](int error) {}); } #endif #if 0 void diagnose_handler_with_invalid_move_only_error_arg() { // The error type must match that produced by the prior. fit::make_promise([] { return fit::error(move_only{}); }) .or_else([](move_only error) {}); } #endif BEGIN_TEST_CASE(promise_tests) RUN_TEST(basics) RUN_TEST(empty_promise) RUN_TEST(invocation) RUN_TEST(take_continuation) RUN_TEST(assignment_and_swap) RUN_TEST(comparison_with_nullptr) RUN_TEST(make_promise) RUN_TEST(make_promise_with_continuation) RUN_TEST(then_combinator) RUN_TEST(and_then_combinator) RUN_TEST(or_else_combinator) RUN_TEST(inspect_combinator) RUN_TEST(discard_result_combinator) RUN_TEST(wrap_with_combinator) RUN_TEST(box_combinator) RUN_TEST(join_combinator) // suppress -Wunneeded-internal-declaration (void)handler_invoker_test::result_continuation_lambda; (void)handler_invoker_test::value_continuation_lambda; (void)handler_invoker_test::error_continuation_lambda; (void)is_continuation_test::continuation_lambda; (void)is_continuation_test::invalid_lambda; END_TEST_CASE(promise_tests)