Skip to content

Commit

Permalink
Add unit test for spawn().
Browse files Browse the repository at this point in the history
  • Loading branch information
cbodley authored and chriskohlhoff committed Nov 5, 2024
1 parent 149b7b6 commit 0c5d5b6
Show file tree
Hide file tree
Showing 3 changed files with 260 additions and 0 deletions.
15 changes: 15 additions & 0 deletions asio/src/tests/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,11 @@ check_PROGRAMS += \
endif
endif

if HAVE_BOOST_COROUTINE
check_PROGRAMS += \
unit/spawn
endif

if HAVE_OPENSSL
check_PROGRAMS += \
unit/ssl/context_base \
Expand Down Expand Up @@ -409,6 +414,11 @@ TESTS += \
unit/experimental/parallel_group
endif

if HAVE_BOOST_COROUTINE
TESTS += \
unit/spawn
endif

if HAVE_CXX20
TESTS += \
unit/experimental/promise
Expand Down Expand Up @@ -634,6 +644,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_context
endif

if HAVE_CXX20
unit_experimental_promise_SOURCES = unit/experimental/promise.cpp

Expand Down
1 change: 1 addition & 0 deletions asio/src/tests/unit/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ serial_port_base
signal_set
signal_set_base
socket_base
spawn
static_thread_pool
steady_timer
strand
Expand Down
244 changes: 244 additions & 0 deletions asio/src/tests/unit/spawn.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
//
// spawn.cpp
// ~~~~~~~~~
//
// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com)
// 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"

#if defined(ASIO_HAS_BOOST_CONTEXT_FIBER)

#include <memory>
#include <stdexcept>
#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<void(std::exception_ptr)>(
[&](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<void(std::exception_ptr, int)>(
[&](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");
}
}

std::unique_ptr<int> factory_coroutine(asio::yield_context)
{
return std::unique_ptr<int>(new int(42));
}

void test_spawn_return_move_only()
{
asio::io_context ctx;

std::unique_ptr<int> result;
bool called = false;
asio::spawn(ctx, factory_coroutine,
[&](std::exception_ptr, std::unique_ptr<int> 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",
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)
ASIO_TEST_CASE(test_spawn_return_move_only)
)

#else // defined(ASIO_HAS_BOOST_CONTEXT_FIBER)

ASIO_TEST_SUITE
(
"spawn",
ASIO_TEST_CASE(null_test)
)

#endif // defined(ASIO_HAS_BOOST_CONTEXT_FIBER)

0 comments on commit 0c5d5b6

Please sign in to comment.