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
TearDownContext9 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
spawnStopContext16 void spawnStop()
17 {
18 ctx->spawn(stdexec::just() |
19 stdexec::then([this]() { ctx->request_stop(); }));
20 }
21
runToStopContext22 void runToStop()
23 {
24 spawnStop();
25 ctx->run();
26 }
27
28 std::optional<sdbusplus::async::context> ctx{std::in_place};
29 };
30
TEST_F(Context,RunSimple)31 TEST_F(Context, RunSimple)
32 {
33 runToStop();
34 }
35
TEST_F(Context,SpawnedTask)36 TEST_F(Context, SpawnedTask)
37 {
38 ctx->spawn(stdexec::just());
39 runToStop();
40 }
41
TEST_F(Context,ReentrantRun)42 TEST_F(Context, ReentrantRun)
43 {
44 runToStop();
45 for (int i = 0; i < 100; ++i)
46 {
47 ctx->run();
48 }
49 }
50
TEST_F(Context,SpawnDelayedTask)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
TEST_F(Context,SpawnRecursiveTask)71 TEST_F(Context, SpawnRecursiveTask)
72 {
73 struct _
74 {
75 static auto one(size_t count,
76 size_t& executed) -> 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
TEST_F(Context,DestructMatcherWithPendingAwait)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
TEST_F(Context,DestructMatcherWithPendingAwaitAsTask)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,
129 bool& ran) -> 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