xref: /openbmc/sdbusplus/test/async/context.cpp (revision 77b8aac3)
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(std::execution::just() |
19                    std::execution::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(std::execution::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(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 
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(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 
78 TEST_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 
89     runToStop();
90 
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 * 3);
96 }
97 
98 TEST_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 
126 TEST_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 
142     EXPECT_THROW(runToStop(), sdbusplus::exception::UnhandledStop);
143     EXPECT_NO_THROW(ctx->run());
144     EXPECT_FALSE(ran);
145 }
146 
147 TEST_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 
171     EXPECT_THROW(runToStop(), sdbusplus::exception::UnhandledStop);
172     EXPECT_NO_THROW(ctx->run());
173     EXPECT_FALSE(ran);
174 }
175