From 243106854c0d30c9ccff33b75ccefe8b0a269223 Mon Sep 17 00:00:00 2001 From: Casey Bodley Date: Wed, 9 Oct 2024 15:42:40 -0400 Subject: [PATCH 1/2] test: add unit tests for spawn() Signed-off-by: Casey Bodley --- asio/src/tests/Makefile.am | 15 +++ asio/src/tests/unit/spawn.cpp | 203 ++++++++++++++++++++++++++++++++++ 2 files changed, 218 insertions(+) create mode 100644 asio/src/tests/unit/spawn.cpp diff --git a/asio/src/tests/Makefile.am b/asio/src/tests/Makefile.am index c051944e23..9a9d19dccb 100644 --- a/asio/src/tests/Makefile.am +++ b/asio/src/tests/Makefile.am @@ -221,6 +221,11 @@ check_PROGRAMS += \ endif endif +if HAVE_BOOST_COROUTINE +check_PROGRAMS += \ + unit/spawn +endif + if HAVE_OPENSSL check_PROGRAMS += \ unit/ssl/context_base \ @@ -406,6 +411,11 @@ TESTS += \ unit/experimental/parallel_group endif +if HAVE_BOOST_COROUTINE +TESTS += \ + unit/spawn +endif + if HAVE_CXX20 TESTS += \ unit/experimental/promise @@ -630,6 +640,11 @@ unit_experimental_concurrent_channel_SOURCES = unit/experimental/concurrent_chan unit_experimental_parallel_group_SOURCES = unit/experimental/parallel_group.cpp endif +if HAVE_BOOST_COROUTINE +unit_spawn_SOURCES = unit/spawn.cpp +unit_spawn_LDADD = -lboost_coroutine -lboost_context +endif + if HAVE_CXX20 unit_experimental_promise_SOURCES = unit/experimental/promise.cpp diff --git a/asio/src/tests/unit/spawn.cpp b/asio/src/tests/unit/spawn.cpp new file mode 100644 index 0000000000..7ae933352b --- /dev/null +++ b/asio/src/tests/unit/spawn.cpp @@ -0,0 +1,203 @@ +// +// spawn.cpp +// ~~~~~~~~~~~~ +// +// Copyright (c) 2024 Casey Bodley (cbodley at redhat dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +// Disable autolinking for unit tests. +#if !defined(BOOST_ALL_NO_LIB) +#define BOOST_ALL_NO_LIB 1 +#endif // !defined(BOOST_ALL_NO_LIB) + +// Test that header file is self-contained. +#include "asio/spawn.hpp" + +#include "unit_test.hpp" + +#include +#include "asio/any_completion_handler.hpp" +#include "asio/bind_cancellation_slot.hpp" +#include "asio/deferred.hpp" +#include "asio/io_context.hpp" +#include "asio/steady_timer.hpp" + +void void_returning_coroutine(asio::yield_context) +{ +} + +int int_returning_coroutine(asio::yield_context) +{ + return 42; +} + +void test_spawn_with_any_completion_handler() +{ + asio::io_context ctx; + + bool called = false; + asio::spawn(ctx, void_returning_coroutine, + asio::any_completion_handler( + [&](std::exception_ptr) + { + called = true; + })); + + ASIO_CHECK(!called); + + ctx.run(); + + ASIO_CHECK(called); + + int result = 0; + asio::spawn(ctx, int_returning_coroutine, + asio::any_completion_handler( + [&](std::exception_ptr, int i) + { + result = i; + })); + + ASIO_CHECK(result == 0); + + ctx.restart(); + ctx.run(); + + ASIO_CHECK(result == 42); +} + +void test_spawn_deferred() +{ + asio::io_context ctx; + + { + bool called = false; + auto fn = asio::spawn(ctx, void_returning_coroutine, asio::deferred); + + fn([&](std::exception_ptr) + { + called = true; + }); + + ASIO_CHECK(!called); + + ctx.poll(); + + ASIO_CHECK(ctx.stopped()); + ASIO_CHECK(called); + } + { + int result = 0; + auto fn = asio::spawn(ctx, int_returning_coroutine, asio::deferred); + + fn([&](std::exception_ptr, int i) + { + result = i; + }); + + ASIO_CHECK(result == 0); + + ctx.restart(); + ctx.poll(); + + ASIO_CHECK(ctx.stopped()); + ASIO_CHECK(result == 42); + } +} + +void sleeping_coroutine(asio::yield_context yield) +{ + asio::steady_timer timer(yield.get_executor(), + asio::steady_timer::time_point::max()); + timer.async_wait(yield); +} + +void test_spawn_cancel() +{ + asio::cancellation_signal sig; + asio::io_context ctx; + + std::exception_ptr result = nullptr; + bool called = false; + asio::spawn(ctx, sleeping_coroutine, + asio::bind_cancellation_slot(sig.slot(), + [&](std::exception_ptr e) + { + result = e; + called = true; + })); + + ctx.poll(); + ASIO_CHECK(!ctx.stopped()); + + ASIO_CHECK(!called); + ASIO_CHECK(result == nullptr); + + sig.emit(asio::cancellation_type::all); + + ctx.poll(); + ASIO_CHECK(ctx.stopped()); + + ASIO_CHECK(called); + ASIO_CHECK(result != nullptr); + try + { + std::rethrow_exception(result); + } + catch (const std::system_error& e) + { + ASIO_CHECK(e.code() == asio::error::operation_aborted); + } + catch (...) + { + ASIO_ERROR("expected system_error"); + } +} + +void throwing_coroutine(asio::yield_context) +{ + throw std::runtime_error("oops"); +} + +void test_spawn_exception() +{ + asio::io_context ctx; + + std::exception_ptr result = nullptr; + bool called = false; + asio::spawn(ctx, throwing_coroutine, + [&](std::exception_ptr e) + { + result = e; + called = true; + }); + + ctx.poll(); + ASIO_CHECK(ctx.stopped()); + + ASIO_CHECK(called); + ASIO_CHECK(result != nullptr); + try + { + std::rethrow_exception(result); + } + catch (const std::runtime_error&) + { + // ok + } + catch (...) + { + ASIO_ERROR("expected runtime_error"); + } +} + +ASIO_TEST_SUITE +( + "spawn", + ASIO_TEST_CASE(test_spawn_with_any_completion_handler) + ASIO_TEST_CASE(test_spawn_deferred) + ASIO_TEST_CASE(test_spawn_cancel) + ASIO_TEST_CASE(test_spawn_exception) +) From e0cb0e07214e247745d46ddae6f50123eb2fe0bb Mon Sep 17 00:00:00 2001 From: Casey Bodley Date: Thu, 17 Oct 2024 23:31:42 -0400 Subject: [PATCH 2/2] test: add failing test_spawn_return_moveonly() add a test case to reproduce https://github.com/chriskohlhoff/asio/issues/1543 Signed-off-by: Casey Bodley --- asio/src/tests/unit/spawn.cpp | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/asio/src/tests/unit/spawn.cpp b/asio/src/tests/unit/spawn.cpp index 7ae933352b..cda7a0d629 100644 --- a/asio/src/tests/unit/spawn.cpp +++ b/asio/src/tests/unit/spawn.cpp @@ -193,6 +193,32 @@ void test_spawn_exception() } } +std::unique_ptr factory_coroutine(asio::yield_context) +{ + return std::make_unique(42); +} + +void test_spawn_return_moveonly() +{ + asio::io_context ctx; + + std::unique_ptr result; + bool called = false; + asio::spawn(ctx, factory_coroutine, + [&](std::exception_ptr, std::unique_ptr r) + { + result = std::move(r); + called = true; + }); + + ctx.poll(); + ASIO_CHECK(ctx.stopped()); + + ASIO_CHECK(called); + ASIO_CHECK(result); + ASIO_CHECK(*result == 42); +} + ASIO_TEST_SUITE ( "spawn", @@ -200,4 +226,5 @@ ASIO_TEST_SUITE ASIO_TEST_CASE(test_spawn_deferred) ASIO_TEST_CASE(test_spawn_cancel) ASIO_TEST_CASE(test_spawn_exception) + ASIO_TEST_CASE(test_spawn_return_moveonly) )