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.reset(); 225 wdog = std::make_unique<Watchdog>(bus, TEST_PATH, event, 226 Watchdog::ActionTargetMap(), 227 std::move(fallback)); 228 EXPECT_EQ(primaryInterval, milliseconds(wdog->interval(primaryIntervalMs))); 229 EXPECT_FALSE(wdog->enabled()); 230 EXPECT_EQ(0, wdog->timeRemaining()); 231 232 // Enable and then verify 233 EXPECT_TRUE(wdog->enabled(true)); 234 235 // Waiting default expiration 236 EXPECT_EQ(primaryInterval - Quantum(1), waitForWatchdog(primaryInterval)); 237 238 // We should now have entered the fallback once the primary expires 239 EXPECT_FALSE(wdog->enabled()); 240 auto remaining = milliseconds(wdog->timeRemaining()); 241 EXPECT_GE(fallbackInterval, remaining); 242 EXPECT_LT(primaryInterval, remaining); 243 EXPECT_FALSE(wdog->timerExpired()); 244 EXPECT_TRUE(wdog->timerEnabled()); 245 246 // We should still be ticking in fallback when setting action or interval 247 auto newInterval = primaryInterval - Quantum(1); 248 auto newIntervalMs = milliseconds(newInterval).count(); 249 EXPECT_EQ(newInterval, milliseconds(wdog->interval(newIntervalMs))); 250 EXPECT_EQ(Watchdog::Action::None, 251 wdog->expireAction(Watchdog::Action::None)); 252 253 EXPECT_FALSE(wdog->enabled()); 254 EXPECT_GE(remaining, milliseconds(wdog->timeRemaining())); 255 EXPECT_LT(primaryInterval, milliseconds(wdog->timeRemaining())); 256 EXPECT_FALSE(wdog->timerExpired()); 257 EXPECT_TRUE(wdog->timerEnabled()); 258 259 // Test that setting the timeRemaining always resets the timer to the 260 // fallback interval 261 EXPECT_EQ(fallback.interval, wdog->timeRemaining(primaryInterval.count())); 262 EXPECT_FALSE(wdog->enabled()); 263 264 remaining = milliseconds(wdog->timeRemaining()); 265 EXPECT_GE(fallbackInterval, remaining); 266 EXPECT_LE(fallbackInterval - Quantum(1), remaining); 267 EXPECT_FALSE(wdog->timerExpired()); 268 EXPECT_TRUE(wdog->timerEnabled()); 269 270 // Waiting fallback expiration 271 EXPECT_EQ(fallbackInterval - Quantum(1), waitForWatchdog(fallbackInterval)); 272 273 // We should now have disabled the watchdog after the fallback expires 274 EXPECT_FALSE(wdog->enabled()); 275 EXPECT_EQ(0, wdog->timeRemaining()); 276 EXPECT_TRUE(wdog->timerExpired()); 277 EXPECT_FALSE(wdog->timerEnabled()); 278 279 // Make sure enabling the watchdog again works 280 EXPECT_TRUE(wdog->enabled(true)); 281 282 // We should have re-entered the primary 283 EXPECT_TRUE(wdog->enabled()); 284 EXPECT_GE(primaryInterval, milliseconds(wdog->timeRemaining())); 285 EXPECT_FALSE(wdog->timerExpired()); 286 EXPECT_TRUE(wdog->timerEnabled()); 287 } 288 289 /** @brief Make sure the watchdog is started and enabled with a fallback 290 * Wait through the initial trip and ensure the fallback is observed 291 * Make sure that we can re-enable the watchdog during fallback 292 */ 293 TEST_F(WdogTest, enableWdogWithFallbackReEnable) 294 { 295 auto primaryInterval = Quantum(5); 296 auto primaryIntervalMs = milliseconds(primaryInterval).count(); 297 auto fallbackInterval = primaryInterval * 2; 298 auto fallbackIntervalMs = milliseconds(fallbackInterval).count(); 299 300 // We need to make a wdog with the right fallback options 301 // The interval is set to be noticeably different from the default 302 // so we can always tell the difference 303 Watchdog::Fallback fallback; 304 fallback.action = Watchdog::Action::PowerOff; 305 fallback.interval = static_cast<uint64_t>(fallbackIntervalMs); 306 fallback.always = false; 307 wdog.reset(); 308 wdog = std::make_unique<Watchdog>(bus, TEST_PATH, event, 309 Watchdog::ActionTargetMap(), 310 std::move(fallback)); 311 EXPECT_EQ(primaryInterval, milliseconds(wdog->interval(primaryIntervalMs))); 312 EXPECT_FALSE(wdog->enabled()); 313 EXPECT_EQ(0, wdog->timeRemaining()); 314 EXPECT_FALSE(wdog->timerExpired()); 315 EXPECT_FALSE(wdog->timerEnabled()); 316 317 // Enable and then verify 318 EXPECT_TRUE(wdog->enabled(true)); 319 320 // Waiting default expiration 321 EXPECT_EQ(primaryInterval - Quantum(1), waitForWatchdog(primaryInterval)); 322 323 // We should now have entered the fallback once the primary expires 324 EXPECT_FALSE(wdog->enabled()); 325 auto remaining = milliseconds(wdog->timeRemaining()); 326 EXPECT_GE(fallbackInterval, remaining); 327 EXPECT_LT(primaryInterval, remaining); 328 EXPECT_FALSE(wdog->timerExpired()); 329 EXPECT_TRUE(wdog->timerEnabled()); 330 331 EXPECT_TRUE(wdog->enabled(true)); 332 333 // We should have re-entered the primary 334 EXPECT_TRUE(wdog->enabled()); 335 EXPECT_GE(primaryInterval, milliseconds(wdog->timeRemaining())); 336 EXPECT_FALSE(wdog->timerExpired()); 337 EXPECT_TRUE(wdog->timerEnabled()); 338 } 339 340 /** @brief Make sure the watchdog is started and enabled with a fallback 341 * Wait through the initial trip and ensure the fallback is observed 342 * Make sure that changing the primary interval and calling reset timer 343 * will enable the primary watchdog with primary interval. 344 */ 345 TEST_F(WdogTest, enableWdogWithFallbackResetTimerEnable) 346 { 347 auto primaryInterval = Quantum(5); 348 auto primaryIntervalMs = milliseconds(primaryInterval).count(); 349 auto fallbackInterval = primaryInterval * 2; 350 auto fallbackIntervalMs = milliseconds(fallbackInterval).count(); 351 auto newInterval = fallbackInterval * 2; 352 auto newIntervalMs = milliseconds(newInterval).count(); 353 354 // We need to make a wdog with the right fallback options 355 // The interval is set to be noticeably different from the default 356 // so we can always tell the difference 357 Watchdog::Fallback fallback; 358 fallback.action = Watchdog::Action::PowerOff; 359 fallback.interval = static_cast<uint64_t>(fallbackIntervalMs); 360 fallback.always = false; 361 wdog.reset(); 362 wdog = std::make_unique<Watchdog>(bus, TEST_PATH, event, 363 Watchdog::ActionTargetMap(), 364 std::move(fallback)); 365 EXPECT_EQ(primaryInterval, milliseconds(wdog->interval(primaryIntervalMs))); 366 EXPECT_FALSE(wdog->enabled()); 367 EXPECT_EQ(0, wdog->timeRemaining()); 368 EXPECT_FALSE(wdog->timerExpired()); 369 EXPECT_FALSE(wdog->timerEnabled()); 370 371 // Enable and then verify 372 EXPECT_TRUE(wdog->enabled(true)); 373 374 // Waiting default expiration 375 EXPECT_EQ(primaryInterval - Quantum(1), waitForWatchdog(primaryInterval)); 376 377 // We should now have entered the fallback once the primary expires 378 EXPECT_FALSE(wdog->enabled()); 379 auto remaining = milliseconds(wdog->timeRemaining()); 380 EXPECT_GE(fallbackInterval, remaining); 381 EXPECT_LT(primaryInterval, remaining); 382 EXPECT_FALSE(wdog->timerExpired()); 383 EXPECT_TRUE(wdog->timerEnabled()); 384 385 // Setting the interval should take effect once resetTimer re-enables wdog 386 EXPECT_EQ(newIntervalMs, wdog->interval(newIntervalMs)); 387 wdog->resetTimeRemaining(true); 388 389 // We should have re-entered the primary 390 EXPECT_TRUE(wdog->enabled()); 391 remaining = milliseconds(wdog->timeRemaining()); 392 EXPECT_GE(newInterval, remaining); 393 EXPECT_LE(newInterval - Quantum(1), remaining); 394 EXPECT_FALSE(wdog->timerExpired()); 395 EXPECT_TRUE(wdog->timerEnabled()); 396 } 397 398 /** @brief Make sure the watchdog is started and with a fallback without 399 * sending an enable 400 * Then enable the watchdog 401 * Wait through the initial trip and ensure the fallback is observed 402 * Make sure that fallback runs to completion and ensure the watchdog 403 * is in the fallback state again 404 */ 405 TEST_F(WdogTest, enableWdogWithFallbackAlways) 406 { 407 auto primaryInterval = Quantum(5); 408 auto primaryIntervalMs = milliseconds(primaryInterval).count(); 409 auto fallbackInterval = primaryInterval * 2; 410 auto fallbackIntervalMs = milliseconds(fallbackInterval).count(); 411 412 // We need to make a wdog with the right fallback options 413 // The interval is set to be noticeably different from the default 414 // so we can always tell the difference 415 Watchdog::Fallback fallback; 416 fallback.action = Watchdog::Action::PowerOff; 417 fallback.interval = static_cast<uint64_t>(fallbackIntervalMs); 418 fallback.always = true; 419 wdog.reset(); 420 wdog = std::make_unique<Watchdog>(bus, TEST_PATH, event, 421 Watchdog::ActionTargetMap(), fallback, 422 milliseconds(TEST_MIN_INTERVAL).count()); 423 424 // Make sure defualt interval is biggger than min interval 425 EXPECT_LT(milliseconds((TEST_MIN_INTERVAL).count()), 426 milliseconds(wdog->interval())); 427 428 EXPECT_EQ(primaryInterval, milliseconds(wdog->interval(primaryIntervalMs))); 429 EXPECT_FALSE(wdog->enabled()); 430 auto remaining = milliseconds(wdog->timeRemaining()); 431 EXPECT_GE(fallbackInterval, remaining); 432 EXPECT_LT(primaryInterval, remaining); 433 EXPECT_FALSE(wdog->timerExpired()); 434 EXPECT_TRUE(wdog->timerEnabled()); 435 436 // Enable and then verify 437 EXPECT_TRUE(wdog->enabled(true)); 438 EXPECT_GE(primaryInterval, milliseconds(wdog->timeRemaining())); 439 440 // Waiting default expiration 441 EXPECT_EQ(primaryInterval - Quantum(1), waitForWatchdog(primaryInterval)); 442 443 // We should now have entered the fallback once the primary expires 444 EXPECT_FALSE(wdog->enabled()); 445 remaining = milliseconds(wdog->timeRemaining()); 446 EXPECT_GE(fallbackInterval, remaining); 447 EXPECT_LT(primaryInterval, remaining); 448 EXPECT_FALSE(wdog->timerExpired()); 449 EXPECT_TRUE(wdog->timerEnabled()); 450 451 // Waiting fallback expiration 452 EXPECT_EQ(fallbackInterval - Quantum(1), waitForWatchdog(fallbackInterval)); 453 454 // We should now enter the fallback again 455 EXPECT_FALSE(wdog->enabled()); 456 remaining = milliseconds(wdog->timeRemaining()); 457 EXPECT_GE(fallbackInterval, remaining); 458 EXPECT_LT(primaryInterval, remaining); 459 EXPECT_FALSE(wdog->timerExpired()); 460 EXPECT_TRUE(wdog->timerEnabled()); 461 } 462 463 /** @brief Test minimal interval 464 * The minimal interval was set 2 seconds 465 * Test that when setting interval to 1s , it is still returning 2s 466 */ 467 TEST_F(WdogTest, verifyMinIntervalSetting) 468 { 469 auto newInterval = Quantum(1); 470 auto newIntervalMs = milliseconds(newInterval).count(); 471 auto minIntervalMs = milliseconds(TEST_MIN_INTERVAL).count(); 472 473 // Check first that the current interval is greater than minInterval 474 EXPECT_LT(minIntervalMs, wdog->interval()); 475 // Check that the interval was not set to smaller value than minInterval 476 EXPECT_EQ(minIntervalMs, wdog->interval(newIntervalMs)); 477 // Check that the interval was not set to smaller value than minInterval 478 EXPECT_EQ(minIntervalMs, wdog->interval()); 479 } 480 481 /** @brief Test minimal interval 482 * Initiate default Watchdog in order to get the default 483 * interval. 484 * Initiate watchdog with minInterval greater than default 485 * interval, and make sure the default interval was set to the 486 * minInterval. 487 */ 488 TEST_F(WdogTest, verifyConstructorMinIntervalSetting) 489 { 490 // Initiate default Watchdog and get the default interval value. 491 wdog.reset(); 492 wdog = std::make_unique<Watchdog>(bus, TEST_PATH, event); 493 auto defaultIntervalMs = wdog->interval(); 494 auto defaultInterval = milliseconds(defaultIntervalMs); 495 auto minInterval = defaultInterval + Quantum(30); 496 auto minIntervalMs = milliseconds(minInterval).count(); 497 498 // We initiate a new Watchdog with min interval greater than the default 499 // intrval 500 wdog.reset(); 501 wdog = std::make_unique<Watchdog>(bus, TEST_PATH, event, 502 Watchdog::ActionTargetMap(), std::nullopt, 503 minIntervalMs); 504 // Check that the interval was set to the minInterval 505 EXPECT_EQ(minIntervalMs, wdog->interval()); 506 507 // Enable and then verify 508 EXPECT_TRUE(wdog->enabled(true)); 509 EXPECT_FALSE(wdog->timerExpired()); 510 EXPECT_TRUE(wdog->timerEnabled()); 511 512 // Set remaining time shorter than minInterval will actually set it to 513 // minInterval 514 auto remaining = milliseconds(wdog->timeRemaining(defaultIntervalMs)); 515 516 // Its possible that we are off by few msecs depending on 517 // how we get scheduled. So checking a range here. 518 EXPECT_TRUE((remaining >= minInterval - Quantum(1)) && 519 (remaining <= minInterval)); 520 521 EXPECT_FALSE(wdog->timerExpired()); 522 EXPECT_TRUE(wdog->timerEnabled()); 523 } 524 525 } // namespace watchdog 526 } // namespace phosphor 527