| #include "watchdog_test.hpp" |
| |
| #include <chrono> |
| #include <memory> |
| #include <utility> |
| |
| using namespace phosphor::watchdog; |
| |
| seconds WdogTest::waitForWatchdog(seconds timeLimit) |
| { |
| auto previousTimeRemaining = wdog->timeRemaining(); |
| auto ret = 0s; |
| while (ret < timeLimit && previousTimeRemaining >= wdog->timeRemaining() && |
| wdog->timerEnabled()) |
| { |
| previousTimeRemaining = wdog->timeRemaining(); |
| |
| // Returns -0- on timeout and positive number on dispatch |
| auto sleepTime = 1s; |
| if (!sd_event_run(eventP.get(), microseconds(sleepTime).count())) |
| { |
| ret += sleepTime; |
| } |
| } |
| |
| return ret; |
| } |
| |
| /** @brief Make sure that watchdog is started and not enabled */ |
| TEST_F(WdogTest, createWdogAndDontEnable) |
| { |
| EXPECT_FALSE(wdog->enabled()); |
| EXPECT_EQ(0, wdog->timeRemaining()); |
| EXPECT_FALSE(wdog->timerExpired()); |
| EXPECT_FALSE(wdog->timerEnabled()); |
| |
| // We should be able to configure persistent properties |
| // while disabled |
| auto newAction = Watchdog::Action::PowerOff; |
| EXPECT_EQ(newAction, wdog->expireAction(newAction)); |
| auto newIntervalMs = milliseconds(defaultInterval * 2).count(); |
| EXPECT_EQ(newIntervalMs, wdog->interval(newIntervalMs)); |
| |
| EXPECT_EQ(newAction, wdog->expireAction()); |
| EXPECT_EQ(newIntervalMs, wdog->interval()); |
| |
| // We won't be able to configure timeRemaining |
| EXPECT_EQ(0, wdog->timeRemaining(1000)); |
| EXPECT_EQ(0, wdog->timeRemaining()); |
| |
| // Timer should not have become enabled |
| EXPECT_FALSE(wdog->enabled()); |
| EXPECT_EQ(0, wdog->timeRemaining()); |
| EXPECT_FALSE(wdog->timerExpired()); |
| EXPECT_FALSE(wdog->timerEnabled()); |
| } |
| |
| /** @brief Make sure that watchdog is started and enabled */ |
| TEST_F(WdogTest, createWdogAndEnable) |
| { |
| // Enable and then verify |
| EXPECT_TRUE(wdog->enabled(true)); |
| EXPECT_FALSE(wdog->timerExpired()); |
| EXPECT_TRUE(wdog->timerEnabled()); |
| |
| // Get the configured interval |
| auto remaining = milliseconds(wdog->timeRemaining()); |
| |
| // Its possible that we are off by few msecs depending on |
| // how we get scheduled. So checking a range here. |
| EXPECT_TRUE((remaining >= defaultInterval - defaultDrift) && |
| (remaining <= defaultInterval)); |
| |
| EXPECT_FALSE(wdog->timerExpired()); |
| EXPECT_TRUE(wdog->timerEnabled()); |
| } |
| |
| /** @brief Make sure that watchdog is started and enabled. |
| * Later, disable watchdog |
| */ |
| TEST_F(WdogTest, createWdogAndEnableThenDisable) |
| { |
| // Enable and then verify |
| EXPECT_TRUE(wdog->enabled(true)); |
| |
| // Disable and then verify |
| EXPECT_FALSE(wdog->enabled(false)); |
| EXPECT_FALSE(wdog->enabled()); |
| EXPECT_EQ(0, wdog->timeRemaining()); |
| EXPECT_FALSE(wdog->timerExpired()); |
| EXPECT_FALSE(wdog->timerEnabled()); |
| } |
| |
| /** @brief Make sure that watchdog is started and enabled. |
| * Wait for 5 seconds and make sure that the remaining |
| * time shows 25 seconds. |
| */ |
| TEST_F(WdogTest, enableWdogAndWait5Seconds) |
| { |
| // Enable and then verify |
| EXPECT_TRUE(wdog->enabled(true)); |
| |
| // Sleep for 5 seconds |
| auto sleepTime = 5s; |
| std::this_thread::sleep_for(sleepTime); |
| |
| // Get the remaining time again and expectation is that we get 25s |
| auto remaining = milliseconds(wdog->timeRemaining()); |
| auto expected = defaultInterval - sleepTime; |
| |
| // Its possible that we are off by few msecs depending on |
| // how we get scheduled. So checking a range here. |
| EXPECT_TRUE((remaining >= expected - defaultDrift) && |
| (remaining <= expected)); |
| EXPECT_FALSE(wdog->timerExpired()); |
| EXPECT_TRUE(wdog->timerEnabled()); |
| } |
| |
| /** @brief Make sure that watchdog is started and enabled. |
| * Wait 1 second and then reset the timer to 5 seconds |
| * and then expect the watchdog to expire in 5 seconds |
| */ |
| TEST_F(WdogTest, enableWdogAndResetTo5Seconds) |
| { |
| // Enable and then verify |
| EXPECT_TRUE(wdog->enabled(true)); |
| |
| // Sleep for 1 second |
| std::this_thread::sleep_for(1s); |
| |
| // Next timer will expire in 5 seconds from now. |
| auto expireTime = 5s; |
| auto expireTimeMs = milliseconds(expireTime).count(); |
| EXPECT_EQ(expireTimeMs, wdog->timeRemaining(expireTimeMs)); |
| |
| // Waiting for expiration |
| EXPECT_EQ(expireTime - 1s, waitForWatchdog(expireTime)); |
| EXPECT_TRUE(wdog->timerExpired()); |
| EXPECT_FALSE(wdog->timerEnabled()); |
| |
| // Make sure secondary callback was not called. |
| EXPECT_FALSE(expired); |
| } |
| |
| /** @brief Make sure the Interval can be updated directly. |
| */ |
| TEST_F(WdogTest, verifyIntervalUpdateReceived) |
| { |
| auto expireTime = 5s; |
| auto expireTimeMs = milliseconds(expireTime).count(); |
| EXPECT_EQ(expireTimeMs, wdog->interval(expireTimeMs)); |
| |
| // Expect an update in the Interval |
| EXPECT_EQ(expireTimeMs, wdog->interval()); |
| } |
| |
| /** @brief Make sure the Interval can be updated while the timer is running. |
| */ |
| TEST_F(WdogTest, verifyIntervalUpdateRunning) |
| { |
| const auto oldInterval = milliseconds(wdog->interval()); |
| const auto newInterval = 5s; |
| |
| EXPECT_TRUE(wdog->enabled(true)); |
| auto remaining = milliseconds(wdog->timeRemaining()); |
| EXPECT_GE(oldInterval, remaining); |
| EXPECT_LE(oldInterval - defaultDrift, remaining); |
| EXPECT_EQ(newInterval, |
| milliseconds(wdog->interval(milliseconds(newInterval).count()))); |
| |
| // Expect only the interval to update |
| remaining = milliseconds(wdog->timeRemaining()); |
| EXPECT_GE(oldInterval, remaining); |
| EXPECT_LE(oldInterval - defaultDrift, remaining); |
| EXPECT_EQ(newInterval, milliseconds(wdog->interval())); |
| |
| // Expect reset to use the new interval |
| wdog->resetTimeRemaining(false); |
| remaining = milliseconds(wdog->timeRemaining()); |
| EXPECT_GE(newInterval, remaining); |
| EXPECT_LE(newInterval - defaultDrift, remaining); |
| } |
| |
| /** @brief Make sure that watchdog is started and enabled. |
| * Wait default interval seconds and make sure that wdog has died |
| */ |
| TEST_F(WdogTest, enableWdogAndWaitTillEnd) |
| { |
| // Enable and then verify |
| EXPECT_TRUE(wdog->enabled(true)); |
| auto expireTime = duration_cast<seconds>(defaultInterval); |
| |
| // Waiting default expiration |
| EXPECT_EQ(expireTime - 1s, waitForWatchdog(expireTime)); |
| |
| EXPECT_FALSE(wdog->enabled()); |
| EXPECT_EQ(0, wdog->timeRemaining()); |
| EXPECT_TRUE(wdog->timerExpired()); |
| EXPECT_FALSE(wdog->timerEnabled()); |
| } |
| |
| /** @brief Make sure the watchdog is started and enabled with a fallback |
| * Wait through the initial trip and ensure the fallback is observed |
| * Make sure that fallback runs to completion and ensure the watchdog |
| * is disabled |
| */ |
| TEST_F(WdogTest, enableWdogWithFallbackTillEnd) |
| { |
| auto primaryInterval = 5s; |
| auto primaryIntervalMs = milliseconds(primaryInterval).count(); |
| auto fallbackInterval = primaryInterval * 2; |
| auto fallbackIntervalMs = milliseconds(fallbackInterval).count(); |
| |
| // We need to make a wdog with the right fallback options |
| // The interval is set to be noticeably different from the default |
| // so we can always tell the difference |
| Watchdog::Fallback fallback{ |
| .action = Watchdog::Action::PowerOff, |
| .interval = static_cast<uint64_t>(fallbackIntervalMs), |
| }; |
| wdog = std::make_unique<Watchdog>(bus, TEST_PATH, eventP, |
| Watchdog::ActionTargetMap(), |
| std::move(fallback)); |
| EXPECT_EQ(primaryInterval, milliseconds(wdog->interval(primaryIntervalMs))); |
| EXPECT_FALSE(wdog->enabled()); |
| EXPECT_EQ(0, wdog->timeRemaining()); |
| |
| // Enable and then verify |
| EXPECT_TRUE(wdog->enabled(true)); |
| |
| // Waiting default expiration |
| EXPECT_EQ(primaryInterval - 1s, waitForWatchdog(primaryInterval)); |
| |
| // We should now have entered the fallback once the primary expires |
| EXPECT_FALSE(wdog->enabled()); |
| auto remaining = milliseconds(wdog->timeRemaining()); |
| EXPECT_GE(fallbackInterval, remaining); |
| EXPECT_LT(primaryInterval, remaining); |
| EXPECT_FALSE(wdog->timerExpired()); |
| EXPECT_TRUE(wdog->timerEnabled()); |
| |
| // We should still be ticking in fallback when setting action or interval |
| auto newInterval = primaryInterval - 1s; |
| auto newIntervalMs = milliseconds(newInterval).count(); |
| EXPECT_EQ(newInterval, milliseconds(wdog->interval(newIntervalMs))); |
| EXPECT_EQ(Watchdog::Action::None, |
| wdog->expireAction(Watchdog::Action::None)); |
| |
| EXPECT_FALSE(wdog->enabled()); |
| EXPECT_GE(remaining, milliseconds(wdog->timeRemaining())); |
| EXPECT_LT(primaryInterval, milliseconds(wdog->timeRemaining())); |
| EXPECT_FALSE(wdog->timerExpired()); |
| EXPECT_TRUE(wdog->timerEnabled()); |
| |
| // Test that setting the timeRemaining always resets the timer to the |
| // fallback interval |
| EXPECT_EQ(fallback.interval, wdog->timeRemaining(primaryInterval.count())); |
| EXPECT_FALSE(wdog->enabled()); |
| |
| remaining = milliseconds(wdog->timeRemaining()); |
| EXPECT_GE(fallbackInterval, remaining); |
| EXPECT_LE(fallbackInterval - defaultDrift, remaining); |
| EXPECT_FALSE(wdog->timerExpired()); |
| EXPECT_TRUE(wdog->timerEnabled()); |
| |
| // Waiting fallback expiration |
| EXPECT_EQ(fallbackInterval - 1s, waitForWatchdog(fallbackInterval)); |
| |
| // We should now have disabled the watchdog after the fallback expires |
| EXPECT_FALSE(wdog->enabled()); |
| EXPECT_EQ(0, wdog->timeRemaining()); |
| EXPECT_TRUE(wdog->timerExpired()); |
| EXPECT_FALSE(wdog->timerEnabled()); |
| |
| // Make sure enabling the watchdog again works |
| EXPECT_TRUE(wdog->enabled(true)); |
| |
| // We should have re-entered the primary |
| EXPECT_TRUE(wdog->enabled()); |
| EXPECT_GE(primaryInterval, milliseconds(wdog->timeRemaining())); |
| EXPECT_FALSE(wdog->timerExpired()); |
| EXPECT_TRUE(wdog->timerEnabled()); |
| } |
| |
| /** @brief Make sure the watchdog is started and enabled with a fallback |
| * Wait through the initial trip and ensure the fallback is observed |
| * Make sure that we can re-enable the watchdog during fallback |
| */ |
| TEST_F(WdogTest, enableWdogWithFallbackReEnable) |
| { |
| auto primaryInterval = 5s; |
| auto primaryIntervalMs = milliseconds(primaryInterval).count(); |
| auto fallbackInterval = primaryInterval * 2; |
| auto fallbackIntervalMs = milliseconds(fallbackInterval).count(); |
| |
| // We need to make a wdog with the right fallback options |
| // The interval is set to be noticeably different from the default |
| // so we can always tell the difference |
| Watchdog::Fallback fallback{ |
| .action = Watchdog::Action::PowerOff, |
| .interval = static_cast<uint64_t>(fallbackIntervalMs), |
| .always = false, |
| }; |
| wdog = std::make_unique<Watchdog>(bus, TEST_PATH, eventP, |
| Watchdog::ActionTargetMap(), |
| std::move(fallback)); |
| EXPECT_EQ(primaryInterval, milliseconds(wdog->interval(primaryIntervalMs))); |
| EXPECT_FALSE(wdog->enabled()); |
| EXPECT_EQ(0, wdog->timeRemaining()); |
| EXPECT_FALSE(wdog->timerExpired()); |
| EXPECT_FALSE(wdog->timerEnabled()); |
| |
| // Enable and then verify |
| EXPECT_TRUE(wdog->enabled(true)); |
| |
| // Waiting default expiration |
| EXPECT_EQ(primaryInterval - 1s, waitForWatchdog(primaryInterval)); |
| |
| // We should now have entered the fallback once the primary expires |
| EXPECT_FALSE(wdog->enabled()); |
| auto remaining = milliseconds(wdog->timeRemaining()); |
| EXPECT_GE(fallbackInterval, remaining); |
| EXPECT_LT(primaryInterval, remaining); |
| EXPECT_FALSE(wdog->timerExpired()); |
| EXPECT_TRUE(wdog->timerEnabled()); |
| |
| EXPECT_TRUE(wdog->enabled(true)); |
| |
| // We should have re-entered the primary |
| EXPECT_TRUE(wdog->enabled()); |
| EXPECT_GE(primaryInterval, milliseconds(wdog->timeRemaining())); |
| EXPECT_FALSE(wdog->timerExpired()); |
| EXPECT_TRUE(wdog->timerEnabled()); |
| } |
| |
| /** @brief Make sure the watchdog is started and with a fallback without |
| * sending an enable |
| * Then enable the watchdog |
| * Wait through the initial trip and ensure the fallback is observed |
| * Make sure that fallback runs to completion and ensure the watchdog |
| * is in the fallback state again |
| */ |
| TEST_F(WdogTest, enableWdogWithFallbackAlways) |
| { |
| auto primaryInterval = 5s; |
| auto primaryIntervalMs = milliseconds(primaryInterval).count(); |
| auto fallbackInterval = primaryInterval * 2; |
| auto fallbackIntervalMs = milliseconds(fallbackInterval).count(); |
| |
| // We need to make a wdog with the right fallback options |
| // The interval is set to be noticeably different from the default |
| // so we can always tell the difference |
| Watchdog::Fallback fallback{ |
| .action = Watchdog::Action::PowerOff, |
| .interval = static_cast<uint64_t>(fallbackIntervalMs), |
| .always = true, |
| }; |
| wdog = std::make_unique<Watchdog>(bus, TEST_PATH, eventP, |
| Watchdog::ActionTargetMap(), |
| std::move(fallback)); |
| EXPECT_EQ(primaryInterval, milliseconds(wdog->interval(primaryIntervalMs))); |
| EXPECT_FALSE(wdog->enabled()); |
| auto remaining = milliseconds(wdog->timeRemaining()); |
| EXPECT_GE(fallbackInterval, remaining); |
| EXPECT_LT(primaryInterval, remaining); |
| EXPECT_FALSE(wdog->timerExpired()); |
| EXPECT_TRUE(wdog->timerEnabled()); |
| |
| // Enable and then verify |
| EXPECT_TRUE(wdog->enabled(true)); |
| EXPECT_GE(primaryInterval, milliseconds(wdog->timeRemaining())); |
| |
| // Waiting default expiration |
| EXPECT_EQ(primaryInterval - 1s, waitForWatchdog(primaryInterval)); |
| |
| // We should now have entered the fallback once the primary expires |
| EXPECT_FALSE(wdog->enabled()); |
| remaining = milliseconds(wdog->timeRemaining()); |
| EXPECT_GE(fallbackInterval, remaining); |
| EXPECT_LT(primaryInterval, remaining); |
| EXPECT_FALSE(wdog->timerExpired()); |
| EXPECT_TRUE(wdog->timerEnabled()); |
| |
| // Waiting fallback expiration |
| EXPECT_EQ(fallbackInterval - 1s, waitForWatchdog(fallbackInterval)); |
| |
| // We should now enter the fallback again |
| EXPECT_FALSE(wdog->enabled()); |
| remaining = milliseconds(wdog->timeRemaining()); |
| EXPECT_GE(fallbackInterval, remaining); |
| EXPECT_LT(primaryInterval, remaining); |
| EXPECT_FALSE(wdog->timerExpired()); |
| EXPECT_TRUE(wdog->timerEnabled()); |
| } |