1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/threading/watchdog.h"
6
7 #include "base/logging.h"
8 #include "base/macros.h"
9 #include "base/synchronization/spin_wait.h"
10 #include "base/threading/platform_thread.h"
11 #include "base/time/time.h"
12 #include "testing/gtest/include/gtest/gtest.h"
13
14 namespace base {
15
16 namespace {
17
18 //------------------------------------------------------------------------------
19 // Provide a derived class to facilitate testing.
20
21 class WatchdogCounter : public Watchdog {
22 public:
WatchdogCounter(const TimeDelta & duration,const std::string & thread_watched_name,bool enabled)23 WatchdogCounter(const TimeDelta& duration,
24 const std::string& thread_watched_name,
25 bool enabled)
26 : Watchdog(duration, thread_watched_name, enabled),
27 alarm_counter_(0) {
28 }
29
30 ~WatchdogCounter() override = default;
31
Alarm()32 void Alarm() override {
33 alarm_counter_++;
34 Watchdog::Alarm();
35 }
36
alarm_counter()37 int alarm_counter() { return alarm_counter_; }
38
39 private:
40 int alarm_counter_;
41
42 DISALLOW_COPY_AND_ASSIGN(WatchdogCounter);
43 };
44
45 class WatchdogTest : public testing::Test {
46 public:
SetUp()47 void SetUp() override { Watchdog::ResetStaticData(); }
48 };
49
50 } // namespace
51
52 //------------------------------------------------------------------------------
53 // Actual tests
54
55 // Minimal constructor/destructor test.
TEST_F(WatchdogTest,StartupShutdownTest)56 TEST_F(WatchdogTest, StartupShutdownTest) {
57 Watchdog watchdog1(TimeDelta::FromMilliseconds(300), "Disabled", false);
58 Watchdog watchdog2(TimeDelta::FromMilliseconds(300), "Enabled", true);
59 }
60
61 // Test ability to call Arm and Disarm repeatedly.
TEST_F(WatchdogTest,ArmDisarmTest)62 TEST_F(WatchdogTest, ArmDisarmTest) {
63 Watchdog watchdog1(TimeDelta::FromMilliseconds(300), "Disabled", false);
64 watchdog1.Arm();
65 watchdog1.Disarm();
66 watchdog1.Arm();
67 watchdog1.Disarm();
68
69 Watchdog watchdog2(TimeDelta::FromMilliseconds(300), "Enabled", true);
70 watchdog2.Arm();
71 watchdog2.Disarm();
72 watchdog2.Arm();
73 watchdog2.Disarm();
74 }
75
76 // Make sure a basic alarm fires when the time has expired.
TEST_F(WatchdogTest,AlarmTest)77 TEST_F(WatchdogTest, AlarmTest) {
78 WatchdogCounter watchdog(TimeDelta::FromMilliseconds(10), "Enabled", true);
79 watchdog.Arm();
80 SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(5),
81 watchdog.alarm_counter() > 0);
82 EXPECT_EQ(1, watchdog.alarm_counter());
83 }
84
85 // Make sure a basic alarm fires when the time has expired.
TEST_F(WatchdogTest,AlarmPriorTimeTest)86 TEST_F(WatchdogTest, AlarmPriorTimeTest) {
87 WatchdogCounter watchdog(TimeDelta(), "Enabled2", true);
88 // Set a time in the past.
89 watchdog.ArmSomeTimeDeltaAgo(TimeDelta::FromSeconds(2));
90 // It should instantly go off, but certainly in less than 5 minutes.
91 SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(5),
92 watchdog.alarm_counter() > 0);
93
94 EXPECT_EQ(1, watchdog.alarm_counter());
95 }
96
97 // Make sure a disable alarm does nothing, even if we arm it.
TEST_F(WatchdogTest,ConstructorDisabledTest)98 TEST_F(WatchdogTest, ConstructorDisabledTest) {
99 WatchdogCounter watchdog(TimeDelta::FromMilliseconds(10), "Disabled", false);
100 watchdog.Arm();
101 // Alarm should not fire, as it was disabled.
102 PlatformThread::Sleep(TimeDelta::FromMilliseconds(500));
103 EXPECT_EQ(0, watchdog.alarm_counter());
104 }
105
106 // Make sure Disarming will prevent firing, even after Arming.
TEST_F(WatchdogTest,DisarmTest)107 TEST_F(WatchdogTest, DisarmTest) {
108 WatchdogCounter watchdog(TimeDelta::FromSeconds(1), "Enabled3", true);
109
110 TimeTicks start = TimeTicks::Now();
111 watchdog.Arm();
112 // Sleep a bit, but not past the alarm point.
113 PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));
114 watchdog.Disarm();
115 TimeTicks end = TimeTicks::Now();
116
117 if (end - start > TimeDelta::FromMilliseconds(500)) {
118 LOG(WARNING) << "100ms sleep took over 500ms, making the results of this "
119 << "timing-sensitive test suspicious. Aborting now.";
120 return;
121 }
122
123 // Alarm should not have fired before it was disarmed.
124 EXPECT_EQ(0, watchdog.alarm_counter());
125
126 // Sleep past the point where it would have fired if it wasn't disarmed,
127 // and verify that it didn't fire.
128 PlatformThread::Sleep(TimeDelta::FromSeconds(1));
129 EXPECT_EQ(0, watchdog.alarm_counter());
130
131 // ...but even after disarming, we can still use the alarm...
132 // Set a time greater than the timeout into the past.
133 watchdog.ArmSomeTimeDeltaAgo(TimeDelta::FromSeconds(10));
134 // It should almost instantly go off, but certainly in less than 5 minutes.
135 SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(5),
136 watchdog.alarm_counter() > 0);
137
138 EXPECT_EQ(1, watchdog.alarm_counter());
139 }
140
141 } // namespace base
142