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