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