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