xref: /openbmc/sdbusplus/test/async/context.cpp (revision 10483c94)
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, ReentrantRun)
38 {
39     runToStop();
40     for (int i = 0; i < 100; ++i)
41     {
42         ctx->run();
43     }
44 }
45 
46 TEST_F(Context, SpawnThrowingTask)
47 {
48     ctx->spawn(std::execution::just() |
49                std::execution::then([]() { throw std::logic_error("Oops"); }));
50 
51     EXPECT_THROW(runToStop(), std::logic_error);
52     ctx->run();
53 }
54 
55 TEST_F(Context, SpawnDelayedTask)
56 {
57     using namespace std::literals;
58     static constexpr auto timeout = 500ms;
59 
60     auto start = std::chrono::steady_clock::now();
61 
62     bool ran = false;
63     ctx->spawn(sdbusplus::async::sleep_for(*ctx, timeout) |
64                std::execution::then([&ran]() { ran = true; }));
65 
66     runToStop();
67 
68     auto stop = std::chrono::steady_clock::now();
69 
70     EXPECT_TRUE(ran);
71     EXPECT_GT(stop - start, timeout);
72     EXPECT_LT(stop - start, timeout * 2);
73 }
74 
75 TEST_F(Context, SpawnRecursiveTask)
76 {
77     struct _
78     {
79         static auto one(size_t count, size_t& executed)
80             -> sdbusplus::async::task<size_t>
81         {
82             if (count)
83             {
84                 ++executed;
85                 co_return (co_await one(count - 1, executed)) + 1;
86             }
87             co_return co_await std::execution::just(0);
88         }
89     };
90 
91     static constexpr size_t count = 100;
92     size_t executed = 0;
93 
94     ctx->spawn(_::one(count, executed) | std::execution::then([=](auto result) {
95                    EXPECT_EQ(result, count);
96                }));
97 
98     runToStop();
99 
100     EXPECT_EQ(executed, count);
101 }
102 
103 TEST_F(Context, DestructMatcherWithPendingAwait)
104 {
105     using namespace std::literals;
106 
107     bool ran = false;
108     auto m = std::make_optional<sdbusplus::async::match>(
109         *ctx, sdbusplus::bus::match::rules::interfacesAdded(
110                   "/this/is/a/bogus/path/for/SpawnMatcher"));
111 
112     // Await the match completion (which will never happen).
113     ctx->spawn(m->next() | std::execution::then([&ran](...) { ran = true; }));
114 
115     // Destruct the match.
116     ctx->spawn(sdbusplus::async::sleep_for(*ctx, 1ms) |
117                std::execution::then([&m](...) { m.reset(); }));
118 
119     EXPECT_THROW(runToStop(), sdbusplus::exception::UnhandledStop);
120     EXPECT_NO_THROW(ctx->run());
121     EXPECT_FALSE(ran);
122 }
123 
124 TEST_F(Context, DestructMatcherWithPendingAwaitAsTask)
125 {
126     using namespace std::literals;
127 
128     auto m = std::make_optional<sdbusplus::async::match>(
129         *ctx, sdbusplus::bus::match::rules::interfacesAdded(
130                   "/this/is/a/bogus/path/for/SpawnMatcher"));
131 
132     struct _
133     {
134         static auto fn(decltype(m->next()) snd, bool& ran)
135             -> sdbusplus::async::task<>
136         {
137             co_await std::move(snd);
138             ran = true;
139             co_return;
140         }
141     };
142 
143     bool ran = false;
144     ctx->spawn(_::fn(m->next(), ran));
145     ctx->spawn(sdbusplus::async::sleep_for(*ctx, 1ms) |
146                std::execution::then([&]() { m.reset(); }));
147 
148     EXPECT_THROW(runToStop(), sdbusplus::exception::UnhandledStop);
149     EXPECT_NO_THROW(ctx->run());
150     EXPECT_FALSE(ran);
151 }
152