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, SpawnThrowingTask) 52 { 53 ctx->spawn(stdexec::just() | 54 stdexec::then([]() { throw std::logic_error("Oops"); })); 55 56 EXPECT_THROW(runToStop(), std::logic_error); 57 ctx->run(); 58 } 59 60 TEST_F(Context, SpawnThrowingCoroutine) 61 { 62 struct _ 63 { 64 static auto one() -> sdbusplus::async::task<> 65 { 66 throw std::logic_error("Oops"); 67 co_return; 68 } 69 }; 70 71 ctx->spawn(_::one()); 72 EXPECT_THROW(runToStop(), std::logic_error); 73 ctx->run(); 74 }; 75 76 TEST_F(Context, SpawnManyThrowingTasks) 77 { 78 static constexpr size_t count = 100; 79 for (size_t i = 0; i < count; ++i) 80 { 81 ctx->spawn(stdexec::just() | 82 stdexec::then([]() { throw std::logic_error("Oops"); })); 83 } 84 spawnStop(); 85 86 for (size_t i = 0; i < count; ++i) 87 { 88 EXPECT_THROW(ctx->run(), std::logic_error); 89 } 90 ctx->run(); 91 } 92 93 TEST_F(Context, SpawnDelayedTask) 94 { 95 using namespace std::literals; 96 static constexpr auto timeout = 500ms; 97 98 auto start = std::chrono::steady_clock::now(); 99 100 bool ran = false; 101 ctx->spawn(sdbusplus::async::sleep_for(*ctx, timeout) | 102 stdexec::then([&ran]() { ran = true; })); 103 104 runToStop(); 105 106 auto stop = std::chrono::steady_clock::now(); 107 108 EXPECT_TRUE(ran); 109 EXPECT_GT(stop - start, timeout); 110 EXPECT_LT(stop - start, timeout * 3); 111 } 112 113 TEST_F(Context, SpawnRecursiveTask) 114 { 115 struct _ 116 { 117 static auto one(size_t count, size_t& executed) 118 -> sdbusplus::async::task<size_t> 119 { 120 if (count) 121 { 122 ++executed; 123 co_return (co_await one(count - 1, executed)) + 1; 124 } 125 co_return co_await stdexec::just(0); 126 } 127 }; 128 129 static constexpr size_t count = 100; 130 size_t executed = 0; 131 132 ctx->spawn(_::one(count, executed) | 133 stdexec::then([=](auto result) { EXPECT_EQ(result, count); })); 134 135 runToStop(); 136 137 EXPECT_EQ(executed, count); 138 } 139 140 TEST_F(Context, DestructMatcherWithPendingAwait) 141 { 142 using namespace std::literals; 143 144 bool ran = false; 145 auto m = std::make_optional<sdbusplus::async::match>( 146 *ctx, sdbusplus::bus::match::rules::interfacesAdded( 147 "/this/is/a/bogus/path/for/SpawnMatcher")); 148 149 // Await the match completion (which will never happen). 150 ctx->spawn(m->next() | stdexec::then([&ran](...) { ran = true; })); 151 152 // Destruct the match. 153 ctx->spawn(sdbusplus::async::sleep_for(*ctx, 1ms) | 154 stdexec::then([&m](...) { m.reset(); })); 155 156 EXPECT_THROW(runToStop(), sdbusplus::exception::UnhandledStop); 157 EXPECT_NO_THROW(ctx->run()); 158 EXPECT_FALSE(ran); 159 } 160 161 TEST_F(Context, DestructMatcherWithPendingAwaitAsTask) 162 { 163 using namespace std::literals; 164 165 auto m = std::make_optional<sdbusplus::async::match>( 166 *ctx, sdbusplus::bus::match::rules::interfacesAdded( 167 "/this/is/a/bogus/path/for/SpawnMatcher")); 168 169 struct _ 170 { 171 static auto fn(decltype(m->next()) snd, bool& ran) 172 -> sdbusplus::async::task<> 173 { 174 co_await std::move(snd); 175 ran = true; 176 co_return; 177 } 178 }; 179 180 bool ran = false; 181 ctx->spawn(_::fn(m->next(), ran)); 182 ctx->spawn(sdbusplus::async::sleep_for(*ctx, 1ms) | 183 stdexec::then([&]() { m.reset(); })); 184 185 EXPECT_THROW(runToStop(), sdbusplus::exception::UnhandledStop); 186 EXPECT_NO_THROW(ctx->run()); 187 EXPECT_FALSE(ran); 188 } 189