|  | // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "base/threading/watchdog.h" | 
|  |  | 
|  | #include "base/logging.h" | 
|  | #include "base/macros.h" | 
|  | #include "base/synchronization/spin_wait.h" | 
|  | #include "base/threading/platform_thread.h" | 
|  | #include "base/time/time.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | namespace base { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | //------------------------------------------------------------------------------ | 
|  | // Provide a derived class to facilitate testing. | 
|  |  | 
|  | class WatchdogCounter : public Watchdog { | 
|  | public: | 
|  | WatchdogCounter(const TimeDelta& duration, | 
|  | const std::string& thread_watched_name, | 
|  | bool enabled) | 
|  | : Watchdog(duration, thread_watched_name, enabled), | 
|  | alarm_counter_(0) { | 
|  | } | 
|  |  | 
|  | ~WatchdogCounter() override = default; | 
|  |  | 
|  | void Alarm() override { | 
|  | alarm_counter_++; | 
|  | Watchdog::Alarm(); | 
|  | } | 
|  |  | 
|  | int alarm_counter() { return alarm_counter_; } | 
|  |  | 
|  | private: | 
|  | int alarm_counter_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(WatchdogCounter); | 
|  | }; | 
|  |  | 
|  | class WatchdogTest : public testing::Test { | 
|  | public: | 
|  | void SetUp() override { Watchdog::ResetStaticData(); } | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | //------------------------------------------------------------------------------ | 
|  | // Actual tests | 
|  |  | 
|  | // Minimal constructor/destructor test. | 
|  | TEST_F(WatchdogTest, StartupShutdownTest) { | 
|  | Watchdog watchdog1(TimeDelta::FromMilliseconds(300), "Disabled", false); | 
|  | Watchdog watchdog2(TimeDelta::FromMilliseconds(300), "Enabled", true); | 
|  | } | 
|  |  | 
|  | // Test ability to call Arm and Disarm repeatedly. | 
|  | TEST_F(WatchdogTest, ArmDisarmTest) { | 
|  | Watchdog watchdog1(TimeDelta::FromMilliseconds(300), "Disabled", false); | 
|  | watchdog1.Arm(); | 
|  | watchdog1.Disarm(); | 
|  | watchdog1.Arm(); | 
|  | watchdog1.Disarm(); | 
|  |  | 
|  | Watchdog watchdog2(TimeDelta::FromMilliseconds(300), "Enabled", true); | 
|  | watchdog2.Arm(); | 
|  | watchdog2.Disarm(); | 
|  | watchdog2.Arm(); | 
|  | watchdog2.Disarm(); | 
|  | } | 
|  |  | 
|  | // Make sure a basic alarm fires when the time has expired. | 
|  | TEST_F(WatchdogTest, AlarmTest) { | 
|  | WatchdogCounter watchdog(TimeDelta::FromMilliseconds(10), "Enabled", true); | 
|  | watchdog.Arm(); | 
|  | SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(5), | 
|  | watchdog.alarm_counter() > 0); | 
|  | EXPECT_EQ(1, watchdog.alarm_counter()); | 
|  | } | 
|  |  | 
|  | // Make sure a basic alarm fires when the time has expired. | 
|  | TEST_F(WatchdogTest, AlarmPriorTimeTest) { | 
|  | WatchdogCounter watchdog(TimeDelta(), "Enabled2", true); | 
|  | // Set a time in the past. | 
|  | watchdog.ArmSomeTimeDeltaAgo(TimeDelta::FromSeconds(2)); | 
|  | // It should instantly go off, but certainly in less than 5 minutes. | 
|  | SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(5), | 
|  | watchdog.alarm_counter() > 0); | 
|  |  | 
|  | EXPECT_EQ(1, watchdog.alarm_counter()); | 
|  | } | 
|  |  | 
|  | // Make sure a disable alarm does nothing, even if we arm it. | 
|  | TEST_F(WatchdogTest, ConstructorDisabledTest) { | 
|  | WatchdogCounter watchdog(TimeDelta::FromMilliseconds(10), "Disabled", false); | 
|  | watchdog.Arm(); | 
|  | // Alarm should not fire, as it was disabled. | 
|  | PlatformThread::Sleep(TimeDelta::FromMilliseconds(500)); | 
|  | EXPECT_EQ(0, watchdog.alarm_counter()); | 
|  | } | 
|  |  | 
|  | // Make sure Disarming will prevent firing, even after Arming. | 
|  | TEST_F(WatchdogTest, DisarmTest) { | 
|  | WatchdogCounter watchdog(TimeDelta::FromSeconds(1), "Enabled3", true); | 
|  |  | 
|  | TimeTicks start = TimeTicks::Now(); | 
|  | watchdog.Arm(); | 
|  | // Sleep a bit, but not past the alarm point. | 
|  | PlatformThread::Sleep(TimeDelta::FromMilliseconds(100)); | 
|  | watchdog.Disarm(); | 
|  | TimeTicks end = TimeTicks::Now(); | 
|  |  | 
|  | if (end - start > TimeDelta::FromMilliseconds(500)) { | 
|  | LOG(WARNING) << "100ms sleep took over 500ms, making the results of this " | 
|  | << "timing-sensitive test suspicious.  Aborting now."; | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Alarm should not have fired before it was disarmed. | 
|  | EXPECT_EQ(0, watchdog.alarm_counter()); | 
|  |  | 
|  | // Sleep past the point where it would have fired if it wasn't disarmed, | 
|  | // and verify that it didn't fire. | 
|  | PlatformThread::Sleep(TimeDelta::FromSeconds(1)); | 
|  | EXPECT_EQ(0, watchdog.alarm_counter()); | 
|  |  | 
|  | // ...but even after disarming, we can still use the alarm... | 
|  | // Set a time greater than the timeout into the past. | 
|  | watchdog.ArmSomeTimeDeltaAgo(TimeDelta::FromSeconds(10)); | 
|  | // It should almost instantly go off, but certainly in less than 5 minutes. | 
|  | SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(5), | 
|  | watchdog.alarm_counter() > 0); | 
|  |  | 
|  | EXPECT_EQ(1, watchdog.alarm_counter()); | 
|  | } | 
|  |  | 
|  | }  // namespace base |