xref: /openbmc/sdbusplus/test/async/context.cpp (revision 663b7b73184a8687c5c9add63cb58e06d3073f01)
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         context.reset();
14     }
15 
16     // Local accessor that makes clang-tidy's flow analysis happy.
17     // It provides a local guard that the optional is engaged.
18     auto ctx() -> sdbusplus::async::context&
19     {
20         if (!context.has_value())
21         {
22             throw std::logic_error("Context is not initialized");
23         }
24         return context.value();
25     }
26 
27     void spawnStop()
28     {
29         ctx().spawn(stdexec::just() |
30                     stdexec::then([this]() { ctx().request_stop(); }));
31     }
32 
33     void runToStop()
34     {
35         spawnStop();
36         ctx().run();
37     }
38 
39     std::optional<sdbusplus::async::context> context{std::in_place};
40 };
41 
42 TEST_F(Context, RunSimple)
43 {
44     runToStop();
45 }
46 
47 TEST_F(Context, SpawnedTask)
48 {
49     ctx().spawn(stdexec::just());
50     runToStop();
51 }
52 
53 TEST_F(Context, ReentrantRun)
54 {
55     runToStop();
56     for (int i = 0; i < 100; ++i)
57     {
58         ctx().run();
59     }
60 }
61 
62 TEST_F(Context, SpawnDelayedTask)
63 {
64     using namespace std::literals;
65     static constexpr auto timeout = 500ms;
66 
67     auto start = std::chrono::steady_clock::now();
68 
69     bool ran = false;
70     ctx().spawn(sdbusplus::async::sleep_for(ctx(), timeout) |
71                 stdexec::then([&ran]() { ran = true; }));
72 
73     runToStop();
74 
75     auto stop = std::chrono::steady_clock::now();
76 
77     EXPECT_TRUE(ran);
78     EXPECT_GT(stop - start, timeout);
79     EXPECT_LT(stop - start, timeout * 3);
80 }
81 
82 TEST_F(Context, SpawnRecursiveTask)
83 {
84     struct _
85     {
86         static auto one(size_t count, size_t& executed)
87             -> sdbusplus::async::task<size_t>
88         {
89             if (count)
90             {
91                 ++executed;
92                 co_return (co_await one(count - 1, executed)) + 1;
93             }
94             co_return co_await stdexec::just(0);
95         }
96     };
97 
98     static constexpr size_t count = 100;
99     size_t executed = 0;
100 
101     ctx().spawn(_::one(count, executed) |
102                 stdexec::then([=](auto result) { EXPECT_EQ(result, count); }));
103 
104     runToStop();
105 
106     EXPECT_EQ(executed, count);
107 }
108 
109 TEST_F(Context, DestructMatcherWithPendingAwait)
110 {
111     using namespace std::literals;
112 
113     bool ran = false;
114     auto m = std::make_optional<sdbusplus::async::match>(
115         ctx(), sdbusplus::bus::match::rules::interfacesAdded(
116                    "/this/is/a/bogus/path/for/SpawnMatcher"));
117 
118     // Await the match completion (which will never happen).
119     ctx().spawn(m->next() | stdexec::then([&ran](...) { ran = true; }));
120 
121     // Destruct the match.
122     ctx().spawn(sdbusplus::async::sleep_for(ctx(), 1ms) |
123                 stdexec::then([&m](...) { m.reset(); }));
124 
125     runToStop();
126     EXPECT_FALSE(ran);
127 }
128 
129 TEST_F(Context, DestructMatcherWithPendingAwaitAsTask)
130 {
131     using namespace std::literals;
132 
133     auto m = std::make_optional<sdbusplus::async::match>(
134         ctx(), sdbusplus::bus::match::rules::interfacesAdded(
135                    "/this/is/a/bogus/path/for/SpawnMatcher"));
136 
137     struct _
138     {
139         static auto fn(decltype(m->next()) snd, bool& ran)
140             -> sdbusplus::async::task<>
141         {
142             co_await std::move(snd);
143             ran = true;
144             co_return;
145         }
146     };
147 
148     bool ran = false;
149     ctx().spawn(_::fn(m->next(), ran));
150     ctx().spawn(sdbusplus::async::sleep_for(ctx(), 1ms) |
151                 stdexec::then([&]() { m.reset(); }));
152 
153     runToStop();
154     EXPECT_FALSE(ran);
155 }
156