1 #include "watchdog.hpp" 2 3 #include <memory> 4 #include <thread> 5 #include <utility> 6 7 namespace phosphor 8 { 9 namespace watchdog 10 { 11 12 WdogTest::Quantum WdogTest::waitForWatchdog(Quantum timeLimit) 13 { 14 auto previousTimeRemaining = wdog->timeRemaining(); 15 auto ret = Quantum(0); 16 while (ret < timeLimit && previousTimeRemaining >= wdog->timeRemaining() && 17 wdog->timerEnabled()) 18 { 19 previousTimeRemaining = wdog->timeRemaining(); 20 21 constexpr auto sleepTime = Quantum(1); 22 if (event.run(sleepTime) == 0) 23 { 24 ret += sleepTime; 25 } 26 } 27 28 return ret; 29 } 30 31 /** @brief Make sure that watchdog is started and not enabled */ 32 TEST_F(WdogTest, createWdogAndDontEnable) 33 { 34 EXPECT_FALSE(wdog->enabled()); 35 EXPECT_EQ(0, wdog->timeRemaining()); 36 EXPECT_FALSE(wdog->timerExpired()); 37 EXPECT_FALSE(wdog->timerEnabled()); 38 39 // We should be able to configure persistent properties 40 // while disabled 41 auto newAction = Watchdog::Action::PowerOff; 42 EXPECT_EQ(newAction, wdog->expireAction(newAction)); 43 auto newIntervalMs = milliseconds(defaultInterval * 2).count(); 44 EXPECT_EQ(newIntervalMs, wdog->interval(newIntervalMs)); 45 46 EXPECT_EQ(newAction, wdog->expireAction()); 47 EXPECT_EQ(newIntervalMs, wdog->interval()); 48 49 // We won't be able to configure timeRemaining 50 EXPECT_EQ(0, wdog->timeRemaining(1000)); 51 EXPECT_EQ(0, wdog->timeRemaining()); 52 53 // Timer should not have become enabled 54 EXPECT_FALSE(wdog->enabled()); 55 EXPECT_EQ(0, wdog->timeRemaining()); 56 EXPECT_FALSE(wdog->timerExpired()); 57 EXPECT_FALSE(wdog->timerEnabled()); 58 } 59 60 /** @brief Make sure that watchdog is started and enabled */ 61 TEST_F(WdogTest, createWdogAndEnable) 62 { 63 // Enable and then verify 64 EXPECT_TRUE(wdog->enabled(true)); 65 EXPECT_FALSE(wdog->timerExpired()); 66 EXPECT_TRUE(wdog->timerEnabled()); 67 68 // Get the configured interval 69 auto remaining = milliseconds(wdog->timeRemaining()); 70 71 // Its possible that we are off by few msecs depending on 72 // how we get scheduled. So checking a range here. 73 EXPECT_TRUE((remaining >= defaultInterval - Quantum(1)) && 74 (remaining <= defaultInterval)); 75 76 EXPECT_FALSE(wdog->timerExpired()); 77 EXPECT_TRUE(wdog->timerEnabled()); 78 } 79 80 /** @brief Make sure that watchdog is started and enabled. 81 * Later, disable watchdog 82 */ 83 TEST_F(WdogTest, createWdogAndEnableThenDisable) 84 { 85 // Enable and then verify 86 EXPECT_TRUE(wdog->enabled(true)); 87 88 // Disable and then verify 89 EXPECT_FALSE(wdog->enabled(false)); 90 EXPECT_FALSE(wdog->enabled()); 91 EXPECT_EQ(0, wdog->timeRemaining()); 92 EXPECT_FALSE(wdog->timerExpired()); 93 EXPECT_FALSE(wdog->timerEnabled()); 94 } 95 96 /** @brief Make sure that watchdog is started and enabled. 97 * Wait for 5 quantums and make sure that the remaining 98 * time shows 5 fewer quantums. 99 */ 100 TEST_F(WdogTest, enableWdogAndWait5Quantums) 101 { 102 // Enable and then verify 103 EXPECT_TRUE(wdog->enabled(true)); 104 105 // Sleep for 5 quantums 106 auto sleepTime = Quantum(2); 107 ASSERT_LT(sleepTime, defaultInterval); 108 std::this_thread::sleep_for(sleepTime); 109 110 // Get the remaining time again and expectation is that we get fewer 111 auto remaining = milliseconds(wdog->timeRemaining()); 112 auto expected = defaultInterval - sleepTime; 113 114 // Its possible that we are off by few msecs depending on 115 // how we get scheduled. So checking a range here. 116 EXPECT_TRUE((remaining >= expected - Quantum(1)) && 117 (remaining <= expected)); 118 EXPECT_FALSE(wdog->timerExpired()); 119 EXPECT_TRUE(wdog->timerEnabled()); 120 } 121 122 /** @brief Make sure that watchdog is started and enabled. 123 * Wait 1 quantum and then reset the timer to 5 quantums 124 * and then expect the watchdog to expire in 5 quantums 125 */ 126 TEST_F(WdogTest, enableWdogAndResetTo5Quantums) 127 { 128 // Enable and then verify 129 EXPECT_TRUE(wdog->enabled(true)); 130 131 // Sleep for 1 second 132 std::this_thread::sleep_for(Quantum(1)); 133 134 // Timer should still be running unexpired 135 EXPECT_FALSE(wdog->timerExpired()); 136 EXPECT_TRUE(wdog->timerEnabled()); 137 138 // Next timer will expire in 5 quantums from now. 139 auto expireTime = Quantum(5); 140 auto expireTimeMs = milliseconds(expireTime).count(); 141 EXPECT_EQ(expireTimeMs, wdog->timeRemaining(expireTimeMs)); 142 143 // Waiting for expiration 144 EXPECT_EQ(expireTime - Quantum(1), waitForWatchdog(expireTime)); 145 EXPECT_TRUE(wdog->timerExpired()); 146 EXPECT_FALSE(wdog->timerEnabled()); 147 } 148 149 /** @brief Make sure the Interval can be updated directly. 150 */ 151 TEST_F(WdogTest, verifyIntervalUpdateReceived) 152 { 153 auto expireTime = Quantum(5); 154 auto expireTimeMs = milliseconds(expireTime).count(); 155 EXPECT_EQ(expireTimeMs, wdog->interval(expireTimeMs)); 156 157 // Expect an update in the Interval 158 EXPECT_EQ(expireTimeMs, wdog->interval()); 159 } 160 161 /** @brief Make sure the Interval can be updated while the timer is running. 162 */ 163 TEST_F(WdogTest, verifyIntervalUpdateRunning) 164 { 165 const auto oldInterval = milliseconds(wdog->interval()); 166 const auto newInterval = 5s; 167 168 EXPECT_TRUE(wdog->enabled(true)); 169 auto remaining = milliseconds(wdog->timeRemaining()); 170 EXPECT_GE(oldInterval, remaining); 171 EXPECT_LE(oldInterval - Quantum(1), remaining); 172 EXPECT_EQ(newInterval, 173 milliseconds(wdog->interval(milliseconds(newInterval).count()))); 174 175 // Expect only the interval to update 176 remaining = milliseconds(wdog->timeRemaining()); 177 EXPECT_GE(oldInterval, remaining); 178 EXPECT_LE(oldInterval - Quantum(1), remaining); 179 EXPECT_EQ(newInterval, milliseconds(wdog->interval())); 180 181 // Expect reset to use the new interval 182 wdog->resetTimeRemaining(false); 183 remaining = milliseconds(wdog->timeRemaining()); 184 EXPECT_GE(newInterval, remaining); 185 EXPECT_LE(newInterval - Quantum(1), remaining); 186 } 187 188 /** @brief Make sure that watchdog is started and enabled. 189 * Wait default interval quantums and make sure that wdog has died 190 */ 191 TEST_F(WdogTest, enableWdogAndWaitTillEnd) 192 { 193 // Enable and then verify 194 EXPECT_TRUE(wdog->enabled(true)); 195 196 // Waiting default expiration 197 EXPECT_EQ(defaultInterval - Quantum(1), waitForWatchdog(defaultInterval)); 198 199 EXPECT_FALSE(wdog->enabled()); 200 EXPECT_EQ(0, wdog->timeRemaining()); 201 EXPECT_TRUE(wdog->timerExpired()); 202 EXPECT_FALSE(wdog->timerEnabled()); 203 } 204 205 /** @brief Make sure the watchdog is started and enabled with a fallback 206 * Wait through the initial trip and ensure the fallback is observed 207 * Make sure that fallback runs to completion and ensure the watchdog 208 * is disabled 209 */ 210 TEST_F(WdogTest, enableWdogWithFallbackTillEnd) 211 { 212 auto primaryInterval = Quantum(5); 213 auto primaryIntervalMs = milliseconds(primaryInterval).count(); 214 auto fallbackInterval = primaryInterval * 2; 215 auto fallbackIntervalMs = milliseconds(fallbackInterval).count(); 216 217 // We need to make a wdog with the right fallback options 218 // The interval is set to be noticeably different from the default 219 // so we can always tell the difference 220 Watchdog::Fallback fallback; 221 fallback.action = Watchdog::Action::PowerOff; 222 fallback.interval = static_cast<uint64_t>(fallbackIntervalMs); 223 fallback.always = false; 224 wdog = std::make_unique<Watchdog>(bus, TEST_PATH, event, 225 Watchdog::ActionTargetMap(), 226 std::move(fallback)); 227 EXPECT_EQ(primaryInterval, milliseconds(wdog->interval(primaryIntervalMs))); 228 EXPECT_FALSE(wdog->enabled()); 229 EXPECT_EQ(0, wdog->timeRemaining()); 230 231 // Enable and then verify 232 EXPECT_TRUE(wdog->enabled(true)); 233 234 // Waiting default expiration 235 EXPECT_EQ(primaryInterval - Quantum(1), waitForWatchdog(primaryInterval)); 236 237 // We should now have entered the fallback once the primary expires 238 EXPECT_FALSE(wdog->enabled()); 239 auto remaining = milliseconds(wdog->timeRemaining()); 240 EXPECT_GE(fallbackInterval, remaining); 241 EXPECT_LT(primaryInterval, remaining); 242 EXPECT_FALSE(wdog->timerExpired()); 243 EXPECT_TRUE(wdog->timerEnabled()); 244 245 // We should still be ticking in fallback when setting action or interval 246 auto newInterval = primaryInterval - Quantum(1); 247 auto newIntervalMs = milliseconds(newInterval).count(); 248 EXPECT_EQ(newInterval, milliseconds(wdog->interval(newIntervalMs))); 249 EXPECT_EQ(Watchdog::Action::None, 250 wdog->expireAction(Watchdog::Action::None)); 251 252 EXPECT_FALSE(wdog->enabled()); 253 EXPECT_GE(remaining, milliseconds(wdog->timeRemaining())); 254 EXPECT_LT(primaryInterval, milliseconds(wdog->timeRemaining())); 255 EXPECT_FALSE(wdog->timerExpired()); 256 EXPECT_TRUE(wdog->timerEnabled()); 257 258 // Test that setting the timeRemaining always resets the timer to the 259 // fallback interval 260 EXPECT_EQ(fallback.interval, wdog->timeRemaining(primaryInterval.count())); 261 EXPECT_FALSE(wdog->enabled()); 262 263 remaining = milliseconds(wdog->timeRemaining()); 264 EXPECT_GE(fallbackInterval, remaining); 265 EXPECT_LE(fallbackInterval - Quantum(1), remaining); 266 EXPECT_FALSE(wdog->timerExpired()); 267 EXPECT_TRUE(wdog->timerEnabled()); 268 269 // Waiting fallback expiration 270 EXPECT_EQ(fallbackInterval - Quantum(1), waitForWatchdog(fallbackInterval)); 271 272 // We should now have disabled the watchdog after the fallback expires 273 EXPECT_FALSE(wdog->enabled()); 274 EXPECT_EQ(0, wdog->timeRemaining()); 275 EXPECT_TRUE(wdog->timerExpired()); 276 EXPECT_FALSE(wdog->timerEnabled()); 277 278 // Make sure enabling the watchdog again works 279 EXPECT_TRUE(wdog->enabled(true)); 280 281 // We should have re-entered the primary 282 EXPECT_TRUE(wdog->enabled()); 283 EXPECT_GE(primaryInterval, milliseconds(wdog->timeRemaining())); 284 EXPECT_FALSE(wdog->timerExpired()); 285 EXPECT_TRUE(wdog->timerEnabled()); 286 } 287 288 /** @brief Make sure the watchdog is started and enabled with a fallback 289 * Wait through the initial trip and ensure the fallback is observed 290 * Make sure that we can re-enable the watchdog during fallback 291 */ 292 TEST_F(WdogTest, enableWdogWithFallbackReEnable) 293 { 294 auto primaryInterval = Quantum(5); 295 auto primaryIntervalMs = milliseconds(primaryInterval).count(); 296 auto fallbackInterval = primaryInterval * 2; 297 auto fallbackIntervalMs = milliseconds(fallbackInterval).count(); 298 299 // We need to make a wdog with the right fallback options 300 // The interval is set to be noticeably different from the default 301 // so we can always tell the difference 302 Watchdog::Fallback fallback; 303 fallback.action = Watchdog::Action::PowerOff; 304 fallback.interval = static_cast<uint64_t>(fallbackIntervalMs); 305 fallback.always = false; 306 wdog = std::make_unique<Watchdog>(bus, TEST_PATH, event, 307 Watchdog::ActionTargetMap(), 308 std::move(fallback)); 309 EXPECT_EQ(primaryInterval, milliseconds(wdog->interval(primaryIntervalMs))); 310 EXPECT_FALSE(wdog->enabled()); 311 EXPECT_EQ(0, wdog->timeRemaining()); 312 EXPECT_FALSE(wdog->timerExpired()); 313 EXPECT_FALSE(wdog->timerEnabled()); 314 315 // Enable and then verify 316 EXPECT_TRUE(wdog->enabled(true)); 317 318 // Waiting default expiration 319 EXPECT_EQ(primaryInterval - Quantum(1), waitForWatchdog(primaryInterval)); 320 321 // We should now have entered the fallback once the primary expires 322 EXPECT_FALSE(wdog->enabled()); 323 auto remaining = milliseconds(wdog->timeRemaining()); 324 EXPECT_GE(fallbackInterval, remaining); 325 EXPECT_LT(primaryInterval, remaining); 326 EXPECT_FALSE(wdog->timerExpired()); 327 EXPECT_TRUE(wdog->timerEnabled()); 328 329 EXPECT_TRUE(wdog->enabled(true)); 330 331 // We should have re-entered the primary 332 EXPECT_TRUE(wdog->enabled()); 333 EXPECT_GE(primaryInterval, milliseconds(wdog->timeRemaining())); 334 EXPECT_FALSE(wdog->timerExpired()); 335 EXPECT_TRUE(wdog->timerEnabled()); 336 } 337 338 /** @brief Make sure the watchdog is started and enabled with a fallback 339 * Wait through the initial trip and ensure the fallback is observed 340 * Make sure that changing the primary interval and calling reset timer 341 * will enable the primary watchdog with primary interval. 342 */ 343 TEST_F(WdogTest, enableWdogWithFallbackResetTimerEnable) 344 { 345 auto primaryInterval = Quantum(5); 346 auto primaryIntervalMs = milliseconds(primaryInterval).count(); 347 auto fallbackInterval = primaryInterval * 2; 348 auto fallbackIntervalMs = milliseconds(fallbackInterval).count(); 349 auto newInterval = fallbackInterval * 2; 350 auto newIntervalMs = milliseconds(newInterval).count(); 351 352 // We need to make a wdog with the right fallback options 353 // The interval is set to be noticeably different from the default 354 // so we can always tell the difference 355 Watchdog::Fallback fallback; 356 fallback.action = Watchdog::Action::PowerOff; 357 fallback.interval = static_cast<uint64_t>(fallbackIntervalMs); 358 fallback.always = false; 359 wdog = std::make_unique<Watchdog>(bus, TEST_PATH, event, 360 Watchdog::ActionTargetMap(), 361 std::move(fallback)); 362 EXPECT_EQ(primaryInterval, milliseconds(wdog->interval(primaryIntervalMs))); 363 EXPECT_FALSE(wdog->enabled()); 364 EXPECT_EQ(0, wdog->timeRemaining()); 365 EXPECT_FALSE(wdog->timerExpired()); 366 EXPECT_FALSE(wdog->timerEnabled()); 367 368 // Enable and then verify 369 EXPECT_TRUE(wdog->enabled(true)); 370 371 // Waiting default expiration 372 EXPECT_EQ(primaryInterval - Quantum(1), waitForWatchdog(primaryInterval)); 373 374 // We should now have entered the fallback once the primary expires 375 EXPECT_FALSE(wdog->enabled()); 376 auto remaining = milliseconds(wdog->timeRemaining()); 377 EXPECT_GE(fallbackInterval, remaining); 378 EXPECT_LT(primaryInterval, remaining); 379 EXPECT_FALSE(wdog->timerExpired()); 380 EXPECT_TRUE(wdog->timerEnabled()); 381 382 // Setting the interval should take effect once resetTimer re-enables wdog 383 EXPECT_EQ(newIntervalMs, wdog->interval(newIntervalMs)); 384 wdog->resetTimeRemaining(true); 385 386 // We should have re-entered the primary 387 EXPECT_TRUE(wdog->enabled()); 388 remaining = milliseconds(wdog->timeRemaining()); 389 EXPECT_GE(newInterval, remaining); 390 EXPECT_LE(newInterval - Quantum(1), remaining); 391 EXPECT_FALSE(wdog->timerExpired()); 392 EXPECT_TRUE(wdog->timerEnabled()); 393 } 394 395 /** @brief Make sure the watchdog is started and with a fallback without 396 * sending an enable 397 * Then enable the watchdog 398 * Wait through the initial trip and ensure the fallback is observed 399 * Make sure that fallback runs to completion and ensure the watchdog 400 * is in the fallback state again 401 */ 402 TEST_F(WdogTest, enableWdogWithFallbackAlways) 403 { 404 auto primaryInterval = Quantum(5); 405 auto primaryIntervalMs = milliseconds(primaryInterval).count(); 406 auto fallbackInterval = primaryInterval * 2; 407 auto fallbackIntervalMs = milliseconds(fallbackInterval).count(); 408 409 // We need to make a wdog with the right fallback options 410 // The interval is set to be noticeably different from the default 411 // so we can always tell the difference 412 Watchdog::Fallback fallback; 413 fallback.action = Watchdog::Action::PowerOff; 414 fallback.interval = static_cast<uint64_t>(fallbackIntervalMs); 415 fallback.always = true; 416 wdog = std::make_unique<Watchdog>(bus, TEST_PATH, event, 417 Watchdog::ActionTargetMap(), fallback, 418 milliseconds(TEST_MIN_INTERVAL).count()); 419 420 // Make sure defualt interval is biggger than min interval 421 EXPECT_LT(milliseconds((TEST_MIN_INTERVAL).count()), 422 milliseconds(wdog->interval())); 423 424 EXPECT_EQ(primaryInterval, milliseconds(wdog->interval(primaryIntervalMs))); 425 EXPECT_FALSE(wdog->enabled()); 426 auto remaining = milliseconds(wdog->timeRemaining()); 427 EXPECT_GE(fallbackInterval, remaining); 428 EXPECT_LT(primaryInterval, remaining); 429 EXPECT_FALSE(wdog->timerExpired()); 430 EXPECT_TRUE(wdog->timerEnabled()); 431 432 // Enable and then verify 433 EXPECT_TRUE(wdog->enabled(true)); 434 EXPECT_GE(primaryInterval, milliseconds(wdog->timeRemaining())); 435 436 // Waiting default expiration 437 EXPECT_EQ(primaryInterval - Quantum(1), waitForWatchdog(primaryInterval)); 438 439 // We should now have entered the fallback once the primary expires 440 EXPECT_FALSE(wdog->enabled()); 441 remaining = milliseconds(wdog->timeRemaining()); 442 EXPECT_GE(fallbackInterval, remaining); 443 EXPECT_LT(primaryInterval, remaining); 444 EXPECT_FALSE(wdog->timerExpired()); 445 EXPECT_TRUE(wdog->timerEnabled()); 446 447 // Waiting fallback expiration 448 EXPECT_EQ(fallbackInterval - Quantum(1), waitForWatchdog(fallbackInterval)); 449 450 // We should now enter the fallback again 451 EXPECT_FALSE(wdog->enabled()); 452 remaining = milliseconds(wdog->timeRemaining()); 453 EXPECT_GE(fallbackInterval, remaining); 454 EXPECT_LT(primaryInterval, remaining); 455 EXPECT_FALSE(wdog->timerExpired()); 456 EXPECT_TRUE(wdog->timerEnabled()); 457 } 458 459 /** @brief Test minimal interval 460 * The minimal interval was set 2 seconds 461 * Test that when setting interval to 1s , it is still returning 2s 462 */ 463 TEST_F(WdogTest, verifyMinIntervalSetting) 464 { 465 auto newInterval = Quantum(1); 466 auto newIntervalMs = milliseconds(newInterval).count(); 467 auto minIntervalMs = milliseconds(TEST_MIN_INTERVAL).count(); 468 469 // Check first that the current interval is greater than minInterval 470 EXPECT_LT(minIntervalMs, wdog->interval()); 471 // Check that the interval was not set to smaller value than minInterval 472 EXPECT_EQ(minIntervalMs, wdog->interval(newIntervalMs)); 473 // Check that the interval was not set to smaller value than minInterval 474 EXPECT_EQ(minIntervalMs, wdog->interval()); 475 } 476 477 /** @brief Test minimal interval 478 * Initiate default Watchdog in order to get the default 479 * interval. 480 * Initiate watchdog with minInterval greater than default 481 * interval, and make sure the default interval was set to the 482 * minInterval. 483 */ 484 TEST_F(WdogTest, verifyConstructorMinIntervalSetting) 485 { 486 // Initiate default Watchdog and get the default interval value. 487 wdog = std::make_unique<Watchdog>(bus, TEST_PATH, event); 488 auto defaultInterval = wdog->interval(); 489 auto minInterval = defaultInterval + 100; 490 // We initiate a new Watchdog with min interval greater than the default 491 // intrval 492 wdog = std::make_unique<Watchdog>(bus, TEST_PATH, event, 493 Watchdog::ActionTargetMap(), std::nullopt, 494 minInterval); 495 // Check that the interval was set to the minInterval 496 EXPECT_EQ(minInterval, wdog->interval()); 497 } 498 499 } // namespace watchdog 500 } // namespace phosphor 501