xref: /openbmc/sdbusplus/test/async/context.cpp (revision 3c242ba4c3839ea6dc64c67edb87d1a84d43968a)
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 runToStop()
17     {
18         ctx->spawn(std::execution::just() |
19                    std::execution::then([this]() { ctx->request_stop(); }));
20         ctx->run();
21     }
22 
23     std::optional<sdbusplus::async::context> ctx{std::in_place};
24 };
25 
26 TEST_F(Context, RunSimple)
27 {
28     runToStop();
29 }
30 
31 TEST_F(Context, SpawnedTask)
32 {
33     ctx->spawn(std::execution::just());
34     runToStop();
35 }
36 
37 TEST_F(Context, SpawnDelayedTask)
38 {
39     using namespace std::literals;
40     static constexpr auto timeout = 500ms;
41 
42     auto start = std::chrono::steady_clock::now();
43 
44     bool ran = false;
45     ctx->spawn(sdbusplus::async::sleep_for(*ctx, timeout) |
46                std::execution::then([&ran]() { ran = true; }));
47 
48     runToStop();
49 
50     auto stop = std::chrono::steady_clock::now();
51 
52     EXPECT_TRUE(ran);
53     EXPECT_GT(stop - start, timeout);
54     EXPECT_LT(stop - start, timeout * 2);
55 }
56 
57 TEST_F(Context, SpawnRecursiveTask)
58 {
59     struct _
60     {
61         static auto one(size_t count, size_t& executed)
62             -> sdbusplus::async::task<size_t>
63         {
64             if (count)
65             {
66                 ++executed;
67                 co_return (co_await one(count - 1, executed)) + 1;
68             }
69             co_return co_await std::execution::just(0);
70         }
71     };
72 
73     static constexpr size_t count = 100;
74     size_t executed = 0;
75 
76     ctx->spawn(_::one(count, executed) | std::execution::then([=](auto result) {
77                    EXPECT_EQ(result, count);
78                }));
79 
80     runToStop();
81 
82     EXPECT_EQ(executed, count);
83 }
84 
85 TEST_F(Context, DestructMatcherWithPendingAwait)
86 {
87     using namespace std::literals;
88 
89     bool ran = false;
90     auto m = std::make_optional<sdbusplus::async::match>(
91         *ctx, sdbusplus::bus::match::rules::interfacesAdded(
92                   "/this/is/a/bogus/path/for/SpawnMatcher"));
93 
94     // Await the match completion (which will never happen).
95     ctx->spawn(m->next() | std::execution::then([&ran](...) { ran = true; }));
96 
97     // Destruct the match.
98     ctx->spawn(sdbusplus::async::sleep_for(*ctx, 1ms) |
99                std::execution::then([&m](...) { m.reset(); }));
100 
101     EXPECT_THROW(runToStop(), sdbusplus::exception::UnhandledStop);
102     EXPECT_FALSE(ran);
103 }
104 
105 TEST_F(Context, DestructMatcherWithPendingAwaitAsTask)
106 {
107     using namespace std::literals;
108 
109     auto m = std::make_optional<sdbusplus::async::match>(
110         *ctx, sdbusplus::bus::match::rules::interfacesAdded(
111                   "/this/is/a/bogus/path/for/SpawnMatcher"));
112 
113     struct _
114     {
115         static auto fn(decltype(m->next()) snd, bool& ran)
116             -> sdbusplus::async::task<>
117         {
118             co_await std::move(snd);
119             ran = true;
120             co_return;
121         }
122     };
123 
124     bool ran = false;
125     ctx->spawn(_::fn(m->next(), ran));
126     ctx->spawn(sdbusplus::async::sleep_for(*ctx, 1ms) |
127                std::execution::then([&]() { m.reset(); }));
128 
129     EXPECT_THROW(runToStop(), sdbusplus::exception::UnhandledStop);
130     EXPECT_FALSE(ran);
131 }
132