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