xref: /openbmc/sdbusplus/test/async/context.cpp (revision 5bc767aa)
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