Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions mcpp.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
members = [
"tests/examples/archive",
"tests/examples/asio",
"tests/examples/asio-module",
"tests/examples/build-mcpp",
"tests/examples/cjson",
"tests/examples/core",
Expand Down
419 changes: 419 additions & 0 deletions pkgs/c/chriskohlhoff.asio.lua

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions tests/examples/asio-module/mcpp.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Asio C++23-module consumer test project: `import asio;` (chriskohlhoff.asio,
# Form B inline descriptor, separate-compilation mode). Complements
# tests/examples/asio, which exercises the same upstream in header-only
# `#include <asio.hpp>` form.
[package]
name = "asio-module-tests"
version = "0.1.0"

[indices]
chriskohlhoff = { path = "../../.." }

[dependencies.chriskohlhoff]
asio = "1.38.1"
62 changes: 62 additions & 0 deletions tests/examples/asio-module/tests/core.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Core executor/timer behavior over the module surface: strand FIFO order,
// work-guard-driven run loop, thread_pool, and timer cancellation mapping to
// asio::error::operation_aborted. Module consumers pair `import asio;` with
// `import std;` (no text #include mixing).
import std;
import asio;

int main() {
using namespace std::chrono_literals;

asio::io_context io;
auto guard = asio::make_work_guard(io);
auto serial = asio::make_strand(io);
asio::steady_timer timer(io, 2ms);

std::atomic<int> posted{0};
std::mutex order_mutex;
std::vector<int> order;
bool timer_called = false;

asio::post(serial, [&] {
std::lock_guard lock(order_mutex);
order.push_back(1);
++posted;
});
asio::post(serial, [&] {
std::lock_guard lock(order_mutex);
order.push_back(2);
++posted;
});
timer.async_wait([&](const std::error_code& ec) {
timer_called = !ec;
guard.reset();
});

std::thread worker([&] { io.run(); });
worker.join();
if (posted != 2 || !timer_called || order != std::vector<int>{1, 2}) return 1;

asio::thread_pool pool(2);
std::atomic<int> pooled{0};
asio::post(pool, [&] { ++pooled; });
asio::post(pool, [&] { ++pooled; });
pool.join();
if (pooled != 2) return 2;

asio::io_context cancel_io;
asio::steady_timer cancelled(cancel_io, 1h);
asio::cancellation_signal cancellation;
std::error_code cancelled_ec;
bool cancelled_called = false;
cancelled.async_wait(asio::bind_cancellation_slot(
cancellation.slot(),
[&](const std::error_code& ec) {
cancelled_ec = ec;
cancelled_called = true;
}));
cancellation.emit(asio::cancellation_type::all);
cancel_io.run();

return cancelled_called && cancelled_ec == asio::error::operation_aborted ? 0 : 3;
}
19 changes: 19 additions & 0 deletions tests/examples/asio-module/tests/coroutine.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Coroutine surface: awaitable/co_spawn/use_awaitable/this_coro over the
// module boundary, including a real timer suspension point.
import std;
import asio;

asio::awaitable<int> answer() {
auto ex = co_await asio::this_coro::executor;
asio::steady_timer t(ex, std::chrono::milliseconds(1));
co_await t.async_wait(asio::use_awaitable);
co_return 42;
}

int main() {
asio::io_context io;
int result = 0;
asio::co_spawn(io, answer(), [&](std::exception_ptr, int v) { result = v; });
io.run();
return result == 42 ? 0 : 1;
}
36 changes: 36 additions & 0 deletions tests/examples/asio-module/tests/experimental.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Experimental channel/concurrent_channel/use_promise over the module surface.
// Mirrors tests/examples/asio/tests/experimental.cpp.
import std;
import asio;

int main() {
asio::io_context io;

asio::experimental::channel<void(std::error_code, std::string)> ch(io, 1);
if (!ch.try_send(std::error_code{}, "channel")) return 1;
std::string channel_value;
std::error_code channel_error;
ch.async_receive([&](std::error_code ec, std::string value) {
channel_error = ec;
channel_value = std::move(value);
});

asio::experimental::concurrent_channel<void(std::error_code, int)> concurrent(io, 1);
if (!concurrent.try_send(std::error_code{}, 42)) return 2;
int concurrent_value = 0;
std::error_code concurrent_error;
concurrent.async_receive([&](std::error_code ec, int value) {
concurrent_error = ec;
concurrent_value = value;
});

auto promised = asio::post(io, asio::experimental::use_promise);
bool promise_completed = false;
promised([&] { promise_completed = true; });

io.run();

return !channel_error && channel_value == "channel"
&& !concurrent_error && concurrent_value == 42
&& promise_completed ? 0 : 3;
}
102 changes: 102 additions & 0 deletions tests/examples/asio-module/tests/network.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// TCP (acceptor/socket, async_read/async_write) and UDP (datagram send/receive)
// over the module surface. Mirrors tests/examples/asio/tests/network.cpp.
import std;
import asio;

int main() {
using namespace std::chrono_literals;

// --- TCP echo ---
asio::io_context io;
asio::ip::tcp::acceptor acceptor(io, {asio::ip::address_v4::loopback(), 0});
asio::ip::tcp::socket server(io);
asio::ip::tcp::socket client(io);
asio::steady_timer deadline(io, 5s);

const std::string ping = "ping";
const std::string pong = "pong";
std::array<char, 4> server_data{};
std::array<char, 4> client_data{};
bool accepted = false;
bool connected = false;
bool tcp_done = false;
bool timed_out = false;
int failure = 0;

auto fail = [&](int code) {
if (failure == 0) failure = code;
std::error_code ignored;
acceptor.close(ignored);
server.close(ignored);
client.close(ignored);
deadline.cancel();
};

deadline.async_wait([&](const std::error_code& ec) {
if (!ec) {
timed_out = true;
fail(90);
}
});

acceptor.async_accept(server, [&](const std::error_code& ec) {
if (ec) return fail(1);
accepted = true;
asio::async_read(server, asio::buffer(server_data),
[&](const std::error_code& read_ec, std::size_t n) {
if (read_ec || n != ping.size()
|| std::string(server_data.data(), n) != ping) return fail(2);
asio::async_write(server, asio::buffer(pong),
[&](const std::error_code& write_ec, std::size_t written) {
if (write_ec || written != pong.size()) fail(3);
});
});
});

client.async_connect(
{asio::ip::address_v4::loopback(), acceptor.local_endpoint().port()},
[&](const std::error_code& ec) {
if (ec) return fail(4);
connected = true;
asio::async_write(client, asio::buffer(ping),
[&](const std::error_code& write_ec, std::size_t written) {
if (write_ec || written != ping.size()) return fail(5);
asio::async_read(client, asio::buffer(client_data),
[&](const std::error_code& read_ec, std::size_t n) {
if (read_ec || n != pong.size()
|| std::string(client_data.data(), n) != pong) return fail(6);
tcp_done = true;
deadline.cancel();
});
});
});

io.run();
if (failure || timed_out || !accepted || !connected || !tcp_done) return failure ? failure : 7;

// --- UDP datagram ---
asio::io_context udp_io;
asio::ip::udp::socket receiver(udp_io, {asio::ip::address_v4::loopback(), 0});
asio::ip::udp::socket sender(udp_io, {asio::ip::address_v4::loopback(), 0});
const std::string datagram = "asio-udp";
std::array<char, 8> received{};
asio::ip::udp::endpoint remote;
bool receive_done = false;
bool send_done = false;
std::error_code udp_failure;

receiver.async_receive_from(asio::buffer(received), remote,
[&](const std::error_code& ec, std::size_t n) {
udp_failure = ec;
receive_done = !ec && n == datagram.size()
&& std::string(received.data(), n) == datagram;
});
sender.async_send_to(asio::buffer(datagram), receiver.local_endpoint(),
[&](const std::error_code& ec, std::size_t n) {
if (ec) udp_failure = ec;
send_done = !ec && n == datagram.size();
});

udp_io.run();
return !udp_failure && receive_done && send_done ? 0 : 8;
}
76 changes: 76 additions & 0 deletions tests/examples/asio-module/tests/surface.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Surface/smoke coverage: exported types, completion tokens, and vocabulary
// types are usable across the module boundary.
import std;
import asio;

int main() {
// --- buffer / address (existing) ---
std::array<char, 4> src{'m', 'c', 'p', 'p'};
asio::const_buffer cb = asio::buffer(src);
if (cb.size() != 4) return 1;

std::string dst(4, '\0');
asio::mutable_buffer mb = asio::buffer(dst);
std::memcpy(mb.data(), cb.data(), cb.size());
if (dst != "mcpp") return 2;

asio::ip::address_v4 loopback = asio::ip::address_v4::loopback();
if (loopback.to_string() != "127.0.0.1") return 3;

asio::ip::tcp::endpoint ep(loopback, 8080);
if (ep.port() != 8080) return 4;

// --- execution context hierarchy ---
if (!std::is_base_of_v<asio::execution_context, asio::io_context>) return 5;
if (!std::is_base_of_v<asio::execution_context, asio::system_context>) return 6;
if (!std::is_base_of_v<asio::execution_context, asio::thread_pool>) return 7;

// --- executors ---
static_assert(std::is_class_v<asio::any_io_executor>);
static_assert(std::is_class_v<asio::system_executor>);

// --- error_code typedef ---
static_assert(std::is_same_v<asio::error_code, std::error_code>);

// --- cancellation_type (scoped enum) ---
static_assert(std::is_enum_v<asio::cancellation_type>);
if (static_cast<int>(asio::cancellation_type::all) == 0) return 8;
if (static_cast<int>(asio::cancellation_type::terminal) == 0) return 9;

// --- signal / timer types ---
static_assert(std::is_class_v<asio::signal_set>);
static_assert(std::is_class_v<asio::system_timer>);

// --- completion token variables ---
asio::io_context surface_io;
// detached — compile test for the constexpr variable and its usage
asio::steady_timer t(surface_io, std::chrono::milliseconds(0));
t.async_wait(asio::detached);
static_assert(std::is_same_v<decltype(asio::detached), const asio::detached_t>);

// use_future — accessible as a named variable
auto uf = asio::use_future;
(void)uf;

// deferred
static_assert(std::is_same_v<decltype(asio::deferred), const asio::deferred_t>);

// --- redirect_error ---
std::error_code redirect_ec;
auto redirected = asio::redirect_error(redirect_ec);
(void)redirected;

// --- bind_executor ---
auto bound = asio::bind_executor(asio::system_executor(), []{});
(void)bound;

// --- associated traits ---
static_assert(std::is_class_v<asio::associated_allocator<int>>);
static_assert(std::is_class_v<asio::associated_executor<int>>);
static_assert(std::is_class_v<asio::associated_cancellation_slot<int>>);

// --- error namespace ---
if (asio::error::operation_aborted == std::error_code{}) return 10;

return 0;
}
Loading