xref: /openbmc/sdbusplus/test/async/watchdog.cpp (revision 7c0fb15f6bca1fb107d4c55006a5eed9bd82b325)
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 {
12     WatchdogMock() : sdbusplus::SdBusImpl()
13     {
14         sd_bus_open(&busp);
15     }
16 
17     ~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 
34     WatchdogContext() : ctx(sdbusplus::bus_t(sdbusMock.busp, &sdbusMock)) {}
35 
36     ~WatchdogContext() noexcept override = default;
37 
38     void TearDown() override {}
39 
40     void spawnStop()
41     {
42         ctx.spawn(stdexec::just() |
43                   stdexec::then([this]() { ctx.request_stop(); }));
44     }
45 
46     void runToStop()
47     {
48         spawnStop();
49         ctx.run();
50     }
51 };
52 
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 
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