blob: afae280b7b2376df962a30104d1453da0ad75bc9 [file] [log] [blame]
Patrick Williams73e278b2022-09-16 08:31:36 -05001#include <sdbusplus/async.hpp>
2
3#include <gtest/gtest.h>
4
Patrick Williams78e436f2022-09-21 10:06:20 -05005struct Context : public testing::Test
Patrick Williams73e278b2022-09-16 08:31:36 -05006{
Patrick Williams78e436f2022-09-21 10:06:20 -05007 ~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
Patrick Williams115e9b32022-09-23 16:20:59 -050016 void spawnStop()
Patrick Williams1b7b54c2022-09-21 10:49:45 -050017 {
Patrick Williams3c242ba2022-09-23 09:51:55 -050018 ctx->spawn(std::execution::just() |
19 std::execution::then([this]() { ctx->request_stop(); }));
Patrick Williams115e9b32022-09-23 16:20:59 -050020 }
21
22 void runToStop()
23 {
24 spawnStop();
Patrick Williams3c242ba2022-09-23 09:51:55 -050025 ctx->run();
Patrick Williams1b7b54c2022-09-21 10:49:45 -050026 }
27
Patrick Williams78e436f2022-09-21 10:06:20 -050028 std::optional<sdbusplus::async::context> ctx{std::in_place};
29};
30
31TEST_F(Context, RunSimple)
32{
Patrick Williams1b7b54c2022-09-21 10:49:45 -050033 runToStop();
Patrick Williams73e278b2022-09-16 08:31:36 -050034}
Patrick Williams0139cac2022-09-20 17:04:17 -050035
Patrick Williams78e436f2022-09-21 10:06:20 -050036TEST_F(Context, SpawnedTask)
Patrick Williams0139cac2022-09-20 17:04:17 -050037{
Patrick Williams78e436f2022-09-21 10:06:20 -050038 ctx->spawn(std::execution::just());
Patrick Williams1b7b54c2022-09-21 10:49:45 -050039 runToStop();
Patrick Williams78e436f2022-09-21 10:06:20 -050040}
Patrick Williams0139cac2022-09-20 17:04:17 -050041
Patrick Williams10483c92022-09-23 11:33:39 -050042TEST_F(Context, ReentrantRun)
43{
44 runToStop();
45 for (int i = 0; i < 100; ++i)
46 {
47 ctx->run();
48 }
49}
50
51TEST_F(Context, SpawnThrowingTask)
52{
53 ctx->spawn(std::execution::just() |
54 std::execution::then([]() { throw std::logic_error("Oops"); }));
55
56 EXPECT_THROW(runToStop(), std::logic_error);
57 ctx->run();
58}
59
Patrick Williams115e9b32022-09-23 16:20:59 -050060TEST_F(Context, SpawnManyThrowingTasks)
61{
62 static constexpr size_t count = 100;
63 for (size_t i = 0; i < count; ++i)
64 {
65 ctx->spawn(std::execution::just() | std::execution::then([]() {
66 throw std::logic_error("Oops");
67 }));
68 }
69 spawnStop();
70
71 for (size_t i = 0; i < count; ++i)
72 {
73 EXPECT_THROW(ctx->run(), std::logic_error);
74 }
75 ctx->run();
76}
77
Patrick Williams78e436f2022-09-21 10:06:20 -050078TEST_F(Context, SpawnDelayedTask)
79{
80 using namespace std::literals;
81 static constexpr auto timeout = 500ms;
82
83 auto start = std::chrono::steady_clock::now();
84
85 bool ran = false;
86 ctx->spawn(sdbusplus::async::sleep_for(*ctx, timeout) |
87 std::execution::then([&ran]() { ran = true; }));
88
Patrick Williams1b7b54c2022-09-21 10:49:45 -050089 runToStop();
Patrick Williams78e436f2022-09-21 10:06:20 -050090
91 auto stop = std::chrono::steady_clock::now();
92
93 EXPECT_TRUE(ran);
94 EXPECT_GT(stop - start, timeout);
95 EXPECT_LT(stop - start, timeout * 2);
Patrick Williams0139cac2022-09-20 17:04:17 -050096}
Patrick Williams1b7b54c2022-09-21 10:49:45 -050097
Patrick Williamsd6b05cc2022-09-23 09:38:38 -050098TEST_F(Context, SpawnRecursiveTask)
99{
100 struct _
101 {
102 static auto one(size_t count, size_t& executed)
103 -> sdbusplus::async::task<size_t>
104 {
105 if (count)
106 {
107 ++executed;
108 co_return (co_await one(count - 1, executed)) + 1;
109 }
110 co_return co_await std::execution::just(0);
111 }
112 };
113
114 static constexpr size_t count = 100;
115 size_t executed = 0;
116
117 ctx->spawn(_::one(count, executed) | std::execution::then([=](auto result) {
118 EXPECT_EQ(result, count);
119 }));
120
121 runToStop();
122
123 EXPECT_EQ(executed, count);
124}
125
Patrick Williams1b7b54c2022-09-21 10:49:45 -0500126TEST_F(Context, DestructMatcherWithPendingAwait)
127{
128 using namespace std::literals;
129
130 bool ran = false;
131 auto m = std::make_optional<sdbusplus::async::match>(
132 *ctx, sdbusplus::bus::match::rules::interfacesAdded(
133 "/this/is/a/bogus/path/for/SpawnMatcher"));
134
135 // Await the match completion (which will never happen).
136 ctx->spawn(m->next() | std::execution::then([&ran](...) { ran = true; }));
137
138 // Destruct the match.
139 ctx->spawn(sdbusplus::async::sleep_for(*ctx, 1ms) |
140 std::execution::then([&m](...) { m.reset(); }));
141
Patrick Williams4cfc2842022-09-22 09:53:33 -0500142 EXPECT_THROW(runToStop(), sdbusplus::exception::UnhandledStop);
Patrick Williams10483c92022-09-23 11:33:39 -0500143 EXPECT_NO_THROW(ctx->run());
Patrick Williams1b7b54c2022-09-21 10:49:45 -0500144 EXPECT_FALSE(ran);
145}
146
147TEST_F(Context, DestructMatcherWithPendingAwaitAsTask)
148{
149 using namespace std::literals;
150
151 auto m = std::make_optional<sdbusplus::async::match>(
152 *ctx, sdbusplus::bus::match::rules::interfacesAdded(
153 "/this/is/a/bogus/path/for/SpawnMatcher"));
154
155 struct _
156 {
157 static auto fn(decltype(m->next()) snd, bool& ran)
158 -> sdbusplus::async::task<>
159 {
160 co_await std::move(snd);
161 ran = true;
162 co_return;
163 }
164 };
165
166 bool ran = false;
167 ctx->spawn(_::fn(m->next(), ran));
168 ctx->spawn(sdbusplus::async::sleep_for(*ctx, 1ms) |
169 std::execution::then([&]() { m.reset(); }));
170
Patrick Williams4cfc2842022-09-22 09:53:33 -0500171 EXPECT_THROW(runToStop(), sdbusplus::exception::UnhandledStop);
Patrick Williams10483c92022-09-23 11:33:39 -0500172 EXPECT_NO_THROW(ctx->run());
Patrick Williams1b7b54c2022-09-21 10:49:45 -0500173 EXPECT_FALSE(ran);
174}