/////////////////////////////////////////////////////////////////////////////// // Copyright (c) Lewis Baker // Licenced under MIT license. See LICENSE.txt for details. /////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include "counted.hpp" #include #include #include "doctest/cppcoro_doctest.h" TEST_SUITE_BEGIN("shared_task"); TEST_CASE("awaiting default-constructed task throws broken_promise") { cppcoro::sync_wait([]() -> cppcoro::task<> { CHECK_THROWS_AS(co_await cppcoro::shared_task<>{}, const cppcoro::broken_promise&); }()); } TEST_CASE("coroutine doesn't start executing until awaited") { bool startedExecuting = false; auto f = [&]() -> cppcoro::shared_task<> { startedExecuting = true; co_return; }; auto t = f(); CHECK(!t.is_ready()); CHECK(!startedExecuting); cppcoro::sync_wait([](cppcoro::shared_task<> t) -> cppcoro::task<> { co_await t; }(t)); CHECK(t.is_ready()); CHECK(startedExecuting); } TEST_CASE("result is destroyed when last reference is destroyed") { counted::reset_counts(); { auto t = []() -> cppcoro::shared_task { co_return counted{}; }(); CHECK(counted::active_count() == 0); cppcoro::sync_wait(t); CHECK(counted::active_count() == 1); } CHECK(counted::active_count() == 0); } TEST_CASE("multiple awaiters") { cppcoro::single_consumer_event event; bool startedExecution = false; auto produce = [&]() -> cppcoro::shared_task { startedExecution = true; co_await event; co_return 1; }; auto consume = [](cppcoro::shared_task t) -> cppcoro::task<> { int result = co_await t; CHECK(result == 1); }; auto sharedTask = produce(); cppcoro::sync_wait(cppcoro::when_all_ready( consume(sharedTask), consume(sharedTask), consume(sharedTask), [&]() -> cppcoro::task<> { event.set(); CHECK(sharedTask.is_ready()); co_return; }())); CHECK(sharedTask.is_ready()); } TEST_CASE("waiting on shared_task in loop doesn't cause stack-overflow") { // This test checks that awaiting a shared_task that completes // synchronously doesn't recursively resume the awaiter inside the // call to start executing the task. If it were to do this then we'd // expect that this test would result in failure due to stack-overflow. auto completesSynchronously = []() -> cppcoro::shared_task { co_return 1; }; cppcoro::sync_wait([&]() -> cppcoro::task<> { int result = 0; for (int i = 0; i < 1'000'000; ++i) { result += co_await completesSynchronously(); } CHECK(result == 1'000'000); }()); } TEST_CASE("make_shared_task") { bool startedExecution = false; auto f = [&]() -> cppcoro::task { startedExecution = false; co_return "test"; }; auto t = f(); cppcoro::shared_task sharedT = cppcoro::make_shared_task(std::move(t)); CHECK(!sharedT.is_ready()); CHECK(!startedExecution); auto consume = [](cppcoro::shared_task t) -> cppcoro::task<> { auto x = co_await std::move(t); CHECK(x == "test"); }; cppcoro::sync_wait(cppcoro::when_all_ready( consume(sharedT), consume(sharedT))); } TEST_CASE("make_shared_task of void" * doctest::description{ "Tests that workaround for 'co_return ' bug is operational if required" }) { bool startedExecution = false; auto f = [&]() -> cppcoro::task<> { startedExecution = true; co_return; }; auto t = f(); cppcoro::shared_task<> sharedT = cppcoro::make_shared_task(std::move(t)); CHECK(!sharedT.is_ready()); CHECK(!startedExecution); auto consume = [](cppcoro::shared_task<> t) -> cppcoro::task<> { co_await t; }; auto c1 = consume(sharedT); cppcoro::sync_wait(c1); CHECK(startedExecution); auto c2 = consume(sharedT); cppcoro::sync_wait(c2); CHECK(c1.is_ready()); CHECK(c2.is_ready()); } TEST_CASE("shared_task fmap operator") { cppcoro::single_consumer_event event; int value = 0; auto setNumber = [&]() -> cppcoro::shared_task<> { co_await event; value = 123; }; cppcoro::sync_wait(cppcoro::when_all_ready( [&]() -> cppcoro::task<> { auto numericStringTask = setNumber() | cppcoro::fmap([&]() { return std::to_string(value); }); CHECK(co_await numericStringTask == "123"); }(), [&]() -> cppcoro::task<> { CHECK(value == 0); event.set(); CHECK(value == 123); co_return; }())); } TEST_CASE("shared_task fmap operator") { cppcoro::single_consumer_event event; auto getNumber = [&]() -> cppcoro::shared_task { co_await event; co_return 123; }; cppcoro::sync_wait(cppcoro::when_all_ready( [&]() -> cppcoro::task<> { auto numericStringTask = getNumber() | cppcoro::fmap([](int x) { return std::to_string(x); }); CHECK(co_await numericStringTask == "123"); }(), [&]() -> cppcoro::task<> { event.set(); co_return; }())); } TEST_SUITE_END();