diff --git a/include/continuable/continuable-testing.hpp b/include/continuable/continuable-testing.hpp index 7ca501f..4dcaebf 100644 --- a/include/continuable/continuable-testing.hpp +++ b/include/continuable/continuable-testing.hpp @@ -42,6 +42,13 @@ #define ASSERT_ASYNC_COMPLETION(CONTINUABLE) \ cti::detail::testing::assert_async_completion(CONTINUABLE); +/// Asserts that the final callback of the given continuable was called +/// with any exceptional result. +/// +/// \since version 2.0.0 +#define ASSERT_ASYNC_EXCEPTION_COMPLETION(CONTINUABLE) \ + cti::detail::testing::assert_async_exception_completion(CONTINUABLE); + /// Asserts that the final callback of the given continuable is never called /// with any result. /// @@ -85,6 +92,18 @@ #define ASSERT_ASYNC_BINARY_VALIDATION(VALIDATOR, ...) \ cti::detail::testing::assert_async_binary_validation(VALIDATOR, __VA_ARGS__); +/// Asserts that the continuation was resolved through an error and forwards +/// it's error result to the given validator which can then do assertions on the +/// error result. +/// +/// \note This macro is mainly present for building other assertions +/// relying on custom validation logic. +/// +/// \since version 2.0.0 +#define ASSERT_ASYNC_BINARY_EXCEPTION_VALIDATION(VALIDATOR, ...) \ + cti::detail::testing::assert_async_binary_exception_validation(VALIDATOR, \ + __VA_ARGS__); + /// Expects that the continuable is finished with the given result /// /// ```cpp @@ -99,6 +118,13 @@ ASSERT_ASYNC_BINARY_VALIDATION(cti::detail::testing::expecting_eq_check(), \ __VA_ARGS__) +/// Asserts that the continuable is finished with the given exception +/// +/// \since version 2.0.0 +#define EXPECT_ASYNC_EXCEPTION_RESULT(...) \ + ASSERT_ASYNC_BINARY_EXCEPTION_VALIDATION( \ + cti::detail::testing::expecting_eq_check(), __VA_ARGS__) + /// Asserts that the continuable is finished with the given result /// /// ```cpp @@ -130,11 +156,11 @@ cti::detail::testing::assert_async_types( \ CONTINUABLE, cti::detail::traits::identity<__VA_ARGS__>{}) -/// Asserts that the continuable is finished with the given error -/// TODO +/// Asserts that the continuable is finished with the given exception +/// /// \since version 2.0.0 -#define ASSERT_ASYNC_ERROR(...) \ - ASSERT_ASYNC_BINARY_VALIDATION(cti::detail::testing::asserting_eq_check(), \ - __VA_ARGS__) +#define ASSERT_ASYNC_EXCEPTION_RESULT(...) \ + ASSERT_ASYNC_BINARY_EXCEPTION_VALIDATION( \ + cti::detail::testing::asserting_eq_check(), __VA_ARGS__) #endif // CONTINUABLE_TESTING_HPP_INCLUDED__ diff --git a/include/continuable/detail/testing.hpp b/include/continuable/detail/testing.hpp index c0f21aa..d504aad 100644 --- a/include/continuable/detail/testing.hpp +++ b/include/continuable/detail/testing.hpp @@ -31,9 +31,15 @@ #ifndef CONTINUABLE_DETAIL_TESTING_HPP_INCLUDED__ #define CONTINUABLE_DETAIL_TESTING_HPP_INCLUDED__ +#include +#include + #include -#include +#include +#include +#include +#include namespace cti { namespace detail { @@ -41,25 +47,54 @@ namespace testing { template void assert_async_completion(C&& continuable) { auto called = std::make_shared(false); - std::forward(continuable).then([called](auto&&... args) { - ASSERT_FALSE(*called); - *called = true; + std::forward(continuable) + .then([called](auto&&... args) { + ASSERT_FALSE(*called); + *called = true; - // Workaround for our known GCC bug. - util::unused(std::forward(args)...); - }); + // Workaround for our known GCC bug. + util::unused(std::forward(args)...); + }) + .fail([](cti::error_type /*error*/) { + // ... + FAIL(); + }); + + ASSERT_TRUE(*called); +} + +template +void assert_async_exception_completion(C&& continuable) { + auto called = std::make_shared(false); + std::forward(continuable) + .then([](auto&&... args) { + // Workaround for our known GCC bug. + util::unused(std::forward(args)...); + + // ... + FAIL(); + }) + .fail([called](cti::error_type /*error*/) { + ASSERT_FALSE(*called); + *called = true; + }); ASSERT_TRUE(*called); } template void assert_async_never_completed(C&& continuable) { - std::forward(continuable).then([](auto&&... args) { - // Workaround for our known GCC bug. - util::unused(std::forward(args)...); + std::forward(continuable) + .then([](auto&&... args) { + // Workaround for our known GCC bug. + util::unused(std::forward(args)...); - FAIL(); - }); + FAIL(); + }) + .fail([](cti::error_type /*error*/) { + // ... + FAIL(); + }); } template @@ -94,6 +129,43 @@ void assert_async_binary_validation(V&& validator, C&& continuable, }); } +/// Expects that the continuable is finished with the given arguments +template +void assert_async_binary_exception_validation(V&& validator, C&& continuable, + Args&& expected) { + auto called = std::make_shared(false); + std::forward(continuable) + .then([](auto&&... args) { + // Workaround for our known GCC bug. + util::unused(std::forward(args)...); + + // ... + FAIL(); + }) + .fail([ + called, validator = std::forward(validator), + expected = std::forward(expected) + ](types::error_type error) { + ASSERT_FALSE(*called); + *called = true; + +#if !defined(CONTINUABLE_WITH_CUSTOM_ERROR_TYPE) && \ + !defined(CONTINUABLE_WITH_NO_EXCEPTIONS) + try { + std::rethrow_exception(error); + } catch (std::decay_t const& exception) { + validator(exception, expected); + } catch (...) { + FAIL(); + } +#else + validator(error, expected); +#endif + }); + + ASSERT_TRUE(*called); +} + inline auto expecting_eq_check() { return [](auto&& expected, auto&& actual) { EXPECT_EQ(std::forward(expected), diff --git a/include/continuable/detail/traits.hpp b/include/continuable/detail/traits.hpp index 99674c3..8ac134b 100644 --- a/include/continuable/detail/traits.hpp +++ b/include/continuable/detail/traits.hpp @@ -32,6 +32,7 @@ #define CONTINUABLE_DETAIL_TRAITS_HPP_INCLUDED__ #include +#include #include #include #include @@ -347,8 +348,8 @@ constexpr void static_for_each_in(Sequenceable&& sequenceable, unpack( std::forward(sequenceable), [&](auto&&... entries) mutable { // Apply the consume function to every entry inside the pack - (void)std::initializer_list{0, - ((void)handler(std::forward(entries)), 0)...}; + (void)std::initializer_list{ + 0, ((void)handler(std::forward(entries)), 0)...}; }); } diff --git a/include/continuable/detail/util.hpp b/include/continuable/detail/util.hpp index 2c701f9..7754630 100644 --- a/include/continuable/detail/util.hpp +++ b/include/continuable/detail/util.hpp @@ -31,6 +31,7 @@ #ifndef CONTINUABLE_DETAIL_UTIL_HPP_INCLUDED__ #define CONTINUABLE_DETAIL_UTIL_HPP_INCLUDED__ +#include #include #include #include @@ -45,14 +46,7 @@ namespace detail { namespace util { /// Helper to trick compilers about that a parameter pack is used template -void unused(T&&... args) { - auto use = [](auto&& type) mutable { - (void)type; - return 0; - }; - auto deduce = {0, use(std::forward(args))...}; - (void)deduce; - (void)use; +void unused(T&&...) { } namespace detail { diff --git a/test/unit-test/test-continuable-base-errors.cpp b/test/unit-test/test-continuable-base-errors.cpp index 62a4f4f..70659e0 100644 --- a/test/unit-test/test-continuable-base-errors.cpp +++ b/test/unit-test/test-continuable-base-errors.cpp @@ -21,10 +21,53 @@ SOFTWARE. **/ +#include + +#include + #include "test-continuable.hpp" -TYPED_TEST(single_dimension_tests, are_using_errors) { - /*ASSERT_ASYNC_ERROR(this->supply().then([] { - return; // void - }));*/ +#if !defined(CONTINUABLE_WITH_NO_EXCEPTIONS) +struct test_exception : std::exception { + explicit test_exception() { + } + + bool operator==(test_exception const&) const noexcept { + return true; + } +}; + +static auto get_test_exception_proto() { + return test_exception{}; +} + +static auto supply_test_exception() { + try { + throw get_test_exception_proto(); + } catch (...) { + return std::current_exception(); + } +} +#else +struct my_error_category : std::error_category { + const char* name() const override noexcept {return "generic name"} + + std::string message(int) const override { + return "generic" + } +}; + +static auto get_test_exception_proto() { + static const my_error_category cat; + return std::error_condition(123, cat); +} + +static auto supply_test_exception() { + return get_test_exception_proto(); +} +#endif + +TYPED_TEST(single_dimension_tests, are_using_errors) { + ASSERT_ASYNC_EXCEPTION_COMPLETION( + this->supply_exception(supply_test_exception())); } diff --git a/test/unit-test/test-continuable.hpp b/test/unit-test/test-continuable.hpp index 6c635fe..fc06e03 100644 --- a/test/unit-test/test-continuable.hpp +++ b/test/unit-test/test-continuable.hpp @@ -24,57 +24,15 @@ #ifndef TEST_CONTINUABLE_HPP__ #define TEST_CONTINUABLE_HPP__ -#if UNIT_TEST_STEP >= 3 -#define THIRD_PARTY_TESTS -#endif - -#ifdef THIRD_PARTY_TESTS -// #if _MSC_VER -// #pragma warning(push, 0) -// #endif -#endif // THIRD_PARTY_TESTS - #include -#include "continuable/continuable-base.hpp" -#include "continuable/continuable-testing.hpp" -#include "continuable/continuable.hpp" -#include "gtest/gtest.h" +#include + #include -#ifdef THIRD_PARTY_TESTS - -#include "cxx_function/cxx_function.hpp" - -template -using cxx_function_fn = cxx_function::function; - -template -using cxx_trait_of = - cti::continuable_trait; - -template -using cxx_continuable = typename cxx_trait_of::continuable; - -template -using cxx_function_unique_fn = cxx_function::unique_function; - -template -using unique_cxx_trait_of = - cti::continuable_trait; - -template -using cxx_unique_continuable = - typename unique_cxx_trait_of::continuable; -#endif // THIRD_PARTY_TESTS - -template -using std_trait_of = - cti::continuable_trait; - -template -using std_continuable = typename std_trait_of::continuable; +#include +#include +#include using cti::detail::traits::identity; @@ -96,6 +54,13 @@ auto supplier_of(Args&&... args) { }; } +template +auto exception_supplier_of(Arg&& arg) { + return [arg = std::forward(arg)](auto&& promise) mutable { + promise.set_exception(std::move(arg)); + }; +} + template class continuation_provider : public ::testing::Test, public Provider { public: @@ -115,6 +80,15 @@ public: return this->make(arg_types, hint_types, supplier_of(std::forward(args)...)); } + + template + auto supply_exception(Arg&& arg) { + identity<> arg_types; + auto hint_types = to_hint(arg_types); + + return this->make(arg_types, hint_types, + exception_supplier_of(std::forward(arg))); + } }; inline auto empty_caller() {