Skip to content

Instantly share code, notes, and snippets.

@spchamp
Last active April 6, 2026 08:46
Show Gist options
  • Select an option

  • Save spchamp/ebf376b90bda529e5b8cb02e16b0ad65 to your computer and use it in GitHub Desktop.

Select an option

Save spchamp/ebf376b90bda529e5b8cb02e16b0ad65 to your computer and use it in GitHub Desktop.
macros for async tests with catch2, boost cobalt
// header for include in test.cpp files
//
// [public domain]
//
// usage:
//
// - include this source file in test_async.cpp, ...
//
// - in test_async.cpp, ... define any one or more sections like as follows:
//
// ASYNC_TEST_CASE("name...") { /* coroutine... */ }
//
// ... along with any non-async TEST_CASE or other Catch2 test macros
//
// The ASYNC_TEST_CASE macro should serve as mostly a drop-in replacement
// for Catch2 TEST_CASE, with the added feature of the test body representing
// a coroutine. The coroutine will be run within a cobalt::task<void> via
// cobalt::spawn, using Boost Cobalt.
//
// - use TEST_MAIN() at the end of the source file, thus ensuring Cobalt's
// co_main will be used when running the file's catch2 tests
//
// - Link swith Boost::cobalt, Catch2::Catch2, ...
//
// - Known limitation: No support for test tags in this ASYNC_TEST_CASE
//
// - Known limitation: When the actual test code is included within
// each cobalt::task<void> section, the output from catch2 at the
// command line may not accurately represent the number of test
// assertions in each async test1
//
// cmake e.g:
//
// find_package(Catch2 3.0.0 REQUIRED)
// include(Catch)
//
// find_package(Boost REQUIRED COMPONENTS system)
// find_package(Boost REQUIRED CONFIG COMPONENTS asio cobalt preprocessor)
//
// add_executable(test_async test_async.cpp)
// taget_link_libraries(test_async PRIVATE
// Boost::system Boost::asio Boost::cobalt
// Boost::preprocessor Catch2::Catch2
// )
// target_include_directories(test_async PRIVATE
// ${ANY_VAR}/any_directory_containing_this_file/)
// catch_discover_tests(test_async)
#pragma once
#include <boost/asio/io_context.hpp>
#include <boost/asio/use_future.hpp>
#include <boost/cobalt/main.hpp>
#include <boost/cobalt/task.hpp>
#include <boost/cobalt/this_thread.hpp>
#include <boost/cobalt/spawn.hpp>
#include <boost/preprocessor/cat.hpp>
#include <catch2/catch_session.hpp>
#include <catch2/catch_test_macros.hpp>
// NOLINTBEGIN(misc-unused-alias-decls)
namespace asio = boost::asio;
namespace cobalt = boost::cobalt;
namespace sys = boost::system;
// NOLINTEND(misc-unused-alias-decls)
#ifndef ASYNC_TEST_CASE
# define ASYNC_TEST_CASE(_name, ...) \
cobalt::task<void> BOOST_PP_CAT(test_task_, __LINE__)(); \
TEST_CASE(_name) { \
asio::io_context ctx{BOOST_ASIO_CONCURRENCY_HINT_1}; \
auto exec = ctx.get_executor(); \
cobalt::this_thread::set_executor(exec); \
\
auto ftr = cobalt::spawn(ctx, BOOST_PP_CAT(test_task_, __LINE__)(), \
asio::use_future); \
ctx.run(); \
ftr.wait(); \
} \
cobalt::task<void> BOOST_PP_CAT(test_task_, __LINE__)()
#endif
#ifndef TEST_MAIN
# define TEST_MAIN(...) \
cobalt::main co_main(int argc, char* argv[]) { \
__VA_ARGS__ \
co_return Catch::Session{}.run(argc, argv); \
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment