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