1 #include <sdbusplus/async.hpp> 2 3 #include <gtest/gtest.h> 4 5 struct Context : public testing::Test 6 { 7 ~Context() noexcept = default; 8 9 void TearDown() override 10 { 11 // Destructing the context can throw, so we have to do it in 12 // the TearDown in order to make our destructor noexcept. 13 ctx.reset(); 14 } 15 16 void runToStop() 17 { 18 ctx->run(std::execution::just() | 19 std::execution::then([this]() { ctx->request_stop(); })); 20 } 21 22 std::optional<sdbusplus::async::context> ctx{std::in_place}; 23 }; 24 25 TEST_F(Context, RunSimple) 26 { 27 runToStop(); 28 } 29 30 TEST_F(Context, SpawnedTask) 31 { 32 ctx->spawn(std::execution::just()); 33 runToStop(); 34 } 35 36 TEST_F(Context, SpawnDelayedTask) 37 { 38 using namespace std::literals; 39 static constexpr auto timeout = 500ms; 40 41 auto start = std::chrono::steady_clock::now(); 42 43 bool ran = false; 44 ctx->spawn(sdbusplus::async::sleep_for(*ctx, timeout) | 45 std::execution::then([&ran]() { ran = true; })); 46 47 runToStop(); 48 49 auto stop = std::chrono::steady_clock::now(); 50 51 EXPECT_TRUE(ran); 52 EXPECT_GT(stop - start, timeout); 53 EXPECT_LT(stop - start, timeout * 2); 54 } 55 56 TEST_F(Context, SpawnRecursiveTask) 57 { 58 struct _ 59 { 60 static auto one(size_t count, size_t& executed) 61 -> sdbusplus::async::task<size_t> 62 { 63 if (count) 64 { 65 ++executed; 66 co_return (co_await one(count - 1, executed)) + 1; 67 } 68 co_return co_await std::execution::just(0); 69 } 70 }; 71 72 static constexpr size_t count = 100; 73 size_t executed = 0; 74 75 ctx->spawn(_::one(count, executed) | std::execution::then([=](auto result) { 76 EXPECT_EQ(result, count); 77 })); 78 79 runToStop(); 80 81 EXPECT_EQ(executed, count); 82 } 83 84 TEST_F(Context, DestructMatcherWithPendingAwait) 85 { 86 using namespace std::literals; 87 88 bool ran = false; 89 auto m = std::make_optional<sdbusplus::async::match>( 90 *ctx, sdbusplus::bus::match::rules::interfacesAdded( 91 "/this/is/a/bogus/path/for/SpawnMatcher")); 92 93 // Await the match completion (which will never happen). 94 ctx->spawn(m->next() | std::execution::then([&ran](...) { ran = true; })); 95 96 // Destruct the match. 97 ctx->spawn(sdbusplus::async::sleep_for(*ctx, 1ms) | 98 std::execution::then([&m](...) { m.reset(); })); 99 100 EXPECT_THROW(runToStop(), sdbusplus::exception::UnhandledStop); 101 EXPECT_FALSE(ran); 102 } 103 104 TEST_F(Context, DestructMatcherWithPendingAwaitAsTask) 105 { 106 using namespace std::literals; 107 108 auto m = std::make_optional<sdbusplus::async::match>( 109 *ctx, sdbusplus::bus::match::rules::interfacesAdded( 110 "/this/is/a/bogus/path/for/SpawnMatcher")); 111 112 struct _ 113 { 114 static auto fn(decltype(m->next()) snd, bool& ran) 115 -> sdbusplus::async::task<> 116 { 117 co_await std::move(snd); 118 ran = true; 119 co_return; 120 } 121 }; 122 123 bool ran = false; 124 ctx->spawn(_::fn(m->next(), ran)); 125 ctx->spawn(sdbusplus::async::sleep_for(*ctx, 1ms) | 126 std::execution::then([&]() { m.reset(); })); 127 128 EXPECT_THROW(runToStop(), sdbusplus::exception::UnhandledStop); 129 EXPECT_FALSE(ran); 130 } 131