1 #include <sdbusplus/async.hpp>
2 #include <sdbusplus/bus.hpp>
3 #include <sdbusplus/test/sdbus_mock.hpp>
4
5 #include <gmock/gmock.h>
6 #include <gtest/gtest.h>
7
8 namespace sdbusplus::async
9 {
10 struct WatchdogMock : public sdbusplus::SdBusImpl
11 {
WatchdogMocksdbusplus::async::WatchdogMock12 WatchdogMock() : sdbusplus::SdBusImpl()
13 {
14 sd_bus_open(&busp);
15 }
16
~WatchdogMocksdbusplus::async::WatchdogMock17 ~WatchdogMock()
18 {
19 sd_bus_unref(busp);
20 }
21
22 MOCK_METHOD(int, sd_notify, (int, const char*), (override));
23
24 MOCK_METHOD(int, sd_watchdog_enabled, (int, uint64_t* usec), (override));
25
26 sdbusplus::bus::busp_t busp = nullptr;
27 };
28
29 struct WatchdogContext : public testing::Test
30 {
31 WatchdogMock sdbusMock;
32 sdbusplus::async::context ctx;
33
WatchdogContextsdbusplus::async::WatchdogContext34 WatchdogContext() : ctx(sdbusplus::bus_t(sdbusMock.busp, &sdbusMock)) {}
35
36 ~WatchdogContext() noexcept override = default;
37
TearDownsdbusplus::async::WatchdogContext38 void TearDown() override {}
39
spawnStopsdbusplus::async::WatchdogContext40 void spawnStop()
41 {
42 ctx.spawn(stdexec::just() |
43 stdexec::then([this]() { ctx.request_stop(); }));
44 }
45
runToStopsdbusplus::async::WatchdogContext46 void runToStop()
47 {
48 spawnStop();
49 ctx.run();
50 }
51 };
52
TEST_F(WatchdogContext,WatchdogEnabledAndHeartbeat)53 TEST_F(WatchdogContext, WatchdogEnabledAndHeartbeat)
54 {
55 using namespace testing;
56 using namespace std::literals;
57
58 // Expect sd_watchdog_enabled to be called once and return .1 second
59 EXPECT_CALL(sdbusMock, sd_watchdog_enabled(_, _))
60 .WillOnce([](int, uint64_t* usec) {
61 *usec = 100000; // .1 second
62 return 0;
63 });
64
65 // Expect sd_notify to be called at least once for heartbeat
66 // The watchdog_loop divides the time by 2, so it should be
67 // called every 0.05 seconds.
68 EXPECT_CALL(sdbusMock, sd_notify(_, StrEq("WATCHDOG=1")))
69 .WillRepeatedly(Return(0));
70
71 // Run the context for a short period to allow watchdog_loop to execute
72 // and send a handful of heartbeats.
73 ctx.spawn(sdbusplus::async::sleep_for(ctx, 1s));
74 runToStop();
75
76 // The EXPECT_CALL for sd_notify will verify if it was called as expected.
77 // If the test passes, it means sd_watchdog_enabled was called,
78 // and sd_notify was called for heartbeats.
79 }
80
TEST_F(WatchdogContext,WatchdogDisabled)81 TEST_F(WatchdogContext, WatchdogDisabled)
82 {
83 using namespace testing;
84 using namespace std::literals;
85
86 // Expect sd_watchdog_enabled to be called once and return 0 (disabled)
87 EXPECT_CALL(sdbusMock, sd_watchdog_enabled(_, _))
88 .WillOnce([](int, uint64_t* usec) {
89 *usec = 0; // Watchdog disabled
90 return 0;
91 });
92
93 // Expect sd_notify will NOT be called
94 EXPECT_CALL(sdbusMock, sd_notify(_, _)).Times(0);
95
96 // Run the context. No sleep is needed as watchdog should exit immediately.
97 runToStop();
98 }
99
100 } // namespace sdbusplus::async
101