xref: /openbmc/sdbusplus/test/async/context.cpp (revision b4bae8f6)
1 #include <sdbusplus/async.hpp>
2 
3 #include <gtest/gtest.h>
4 
5 struct Context : public testing::Test
6 {
7     ~Context() noexcept override = 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, SpawnDelayedTask)
52 {
53     using namespace std::literals;
54     static constexpr auto timeout = 500ms;
55 
56     auto start = std::chrono::steady_clock::now();
57 
58     bool ran = false;
59     ctx->spawn(sdbusplus::async::sleep_for(*ctx, timeout) |
60                stdexec::then([&ran]() { ran = true; }));
61 
62     runToStop();
63 
64     auto stop = std::chrono::steady_clock::now();
65 
66     EXPECT_TRUE(ran);
67     EXPECT_GT(stop - start, timeout);
68     EXPECT_LT(stop - start, timeout * 3);
69 }
70 
71 TEST_F(Context, SpawnRecursiveTask)
72 {
73     struct _
74     {
75         static auto one(size_t count, size_t& executed)
76             -> sdbusplus::async::task<size_t>
77         {
78             if (count)
79             {
80                 ++executed;
81                 co_return (co_await one(count - 1, executed)) + 1;
82             }
83             co_return co_await stdexec::just(0);
84         }
85     };
86 
87     static constexpr size_t count = 100;
88     size_t executed = 0;
89 
90     ctx->spawn(_::one(count, executed) |
91                stdexec::then([=](auto result) { EXPECT_EQ(result, count); }));
92 
93     runToStop();
94 
95     EXPECT_EQ(executed, count);
96 }
97 
98 TEST_F(Context, DestructMatcherWithPendingAwait)
99 {
100     using namespace std::literals;
101 
102     bool ran = false;
103     auto m = std::make_optional<sdbusplus::async::match>(
104         *ctx, sdbusplus::bus::match::rules::interfacesAdded(
105                   "/this/is/a/bogus/path/for/SpawnMatcher"));
106 
107     // Await the match completion (which will never happen).
108     ctx->spawn(m->next() | stdexec::then([&ran](...) { ran = true; }));
109 
110     // Destruct the match.
111     ctx->spawn(sdbusplus::async::sleep_for(*ctx, 1ms) |
112                stdexec::then([&m](...) { m.reset(); }));
113 
114     runToStop();
115     EXPECT_FALSE(ran);
116 }
117 
118 TEST_F(Context, DestructMatcherWithPendingAwaitAsTask)
119 {
120     using namespace std::literals;
121 
122     auto m = std::make_optional<sdbusplus::async::match>(
123         *ctx, sdbusplus::bus::match::rules::interfacesAdded(
124                   "/this/is/a/bogus/path/for/SpawnMatcher"));
125 
126     struct _
127     {
128         static auto fn(decltype(m->next()) snd, bool& ran)
129             -> sdbusplus::async::task<>
130         {
131             co_await std::move(snd);
132             ran = true;
133             co_return;
134         }
135     };
136 
137     bool ran = false;
138     ctx->spawn(_::fn(m->next(), ran));
139     ctx->spawn(sdbusplus::async::sleep_for(*ctx, 1ms) |
140                stdexec::then([&]() { m.reset(); }));
141 
142     runToStop();
143     EXPECT_FALSE(ran);
144 }
145