summaryrefslogtreecommitdiffstats
path: root/src/lib/util/io/tests/signal_set_unittest.cc
blob: 9fcd4fda477ea102f8221a3bc4857a5db7df791f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

#include <util/io/signal_set.h>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <gtest/gtest.h>
#include <signal.h>

namespace {

using namespace isc;
using namespace isc::util::io;

/// @brief Test fixture class for @c isc::util::io::SignalSet class.
///
/// This class contains a handler function which records the signal
/// being handled. It allows for checking whether the signal set
/// has invoked the handler for the expected signal.
class SignalSetTest : public ::testing::Test {
public:

    /// @brief Constructor.
    ///
    /// Resets the signal sets and variables being modified by the
    /// signal handler function.
    SignalSetTest()
        : signal_set_(),
          secondary_signal_set_() {
        handler_calls_ = 0;
        signum_ = -1;
    }

    /// @brief Destructor.
    ///
    /// Uninstalls the signals from the signal sets.
    ~SignalSetTest() {
        if (signal_set_) {
            signal_set_->clear();
        }
        if (secondary_signal_set_) {
            secondary_signal_set_->clear();
        }
    }

    /// @brief Signal handler used by unit tests.
    ///
    /// @param signum Signal being handled.
    static void testHandler(int signum) {
        signum_ = signum;
        ++handler_calls_;
    }

    /// @brief Number of handler calls so far.
    static int handler_calls_;
    /// @brief The last signal handled.
    static int signum_;
    /// @brief Test signal set object.
    SignalSetPtr signal_set_;
    /// @brief Second signal set object.
    SignalSetPtr secondary_signal_set_;
};

int SignalSetTest::handler_calls_ = 0;
int SignalSetTest::signum_ = -1;

/// Check that the signals are recorded by the signal handlers.
TEST_F(SignalSetTest, twoSignals) {
    // Register handlers for two signals.
    signal_set_.reset(new SignalSet(SIGHUP, SIGINT));
    // Send SIGHUP signal to the process.
    ASSERT_EQ(0, raise(SIGHUP));
    // The SIGHUP should be the next one in the queue to be handled.
    EXPECT_EQ(SIGHUP, signal_set_->getNext());
    // But, no handlers should have been called yet.
    EXPECT_EQ(0, handler_calls_);
    // Send a different signal.
    ASSERT_EQ(0, raise(SIGINT));
    // The SIGHUP hasn't been handled yet so it should still be the first
    // one in the queue.
    EXPECT_EQ(SIGHUP, signal_set_->getNext());
    // No handlers have been called yet.
    EXPECT_EQ(0, handler_calls_);
    // Raise another SIGHUP before the first one has been handled. The
    // second one should be dropped.
    ASSERT_EQ(0, raise(SIGHUP));
    // Execute the first handler (for SIGHUP).
    signal_set_->handleNext(boost::bind(&SignalSetTest::testHandler, _1));
    // The handler should have been called once and the signal
    // handled should be SIGHUP.
    EXPECT_EQ(1, handler_calls_);
    EXPECT_EQ(SIGHUP, signum_);
    // Next signal to be handled should be SIGINT.
    EXPECT_EQ(SIGINT, signal_set_->getNext());
    signal_set_->handleNext(boost::bind(&SignalSetTest::testHandler, _1));
    EXPECT_EQ(2, handler_calls_);
    EXPECT_EQ(SIGINT, signum_);
    // There should be no more waiting handlers.
    EXPECT_EQ(-1, signal_set_->getNext());
    // Make sure that signals can be unregistered.
    EXPECT_NO_THROW(signal_set_->remove(SIGHUP));
    EXPECT_NO_THROW(signal_set_->remove(SIGINT));
}

/// Check that the signal set can only handle signals owned by it.
TEST_F(SignalSetTest, twoSignalSets) {
    // Register handler for SIGHUP in the first signal set.
    signal_set_.reset(new SignalSet(SIGHUP));
    // Register handler for SIGINT in the second signal set.
    secondary_signal_set_.reset(new SignalSet(SIGINT));
    // Send SIGHUP.
    ASSERT_EQ(0, raise(SIGHUP));
    // Send SIGINT.
    ASSERT_EQ(0, raise(SIGINT));
    // Although the SIGHUP is the first signal received by the process
    // it is not owned by the secondary signal set. The first signal
    // to be handled by the secondary signal set is SIGINT.
    EXPECT_EQ(SIGINT, secondary_signal_set_->getNext());
    // The signal set owns SIGHUP so it should be the next to handle.
    EXPECT_EQ(SIGHUP, signal_set_->getNext());
    // Handle next signal owned by the secondary signal set.
    secondary_signal_set_->handleNext(boost::bind(&SignalSetTest::testHandler,
                                                  _1));
    EXPECT_EQ(1, handler_calls_);
    EXPECT_EQ(SIGINT, signum_);
    // No more signals to be handled for this signal set.
    EXPECT_EQ(-1, secondary_signal_set_->getNext());
    // Handle next signal owned by the signal set.
    signal_set_->handleNext(boost::bind(&SignalSetTest::testHandler, _1));
    EXPECT_EQ(2, handler_calls_);
    EXPECT_EQ(SIGHUP, signum_);
    // No more signals to be handled by this signal set.
    EXPECT_EQ(-1, signal_set_->getNext());
}

// Check that each signal set removes only the signals that it has been used
// to register.
TEST_F(SignalSetTest, remove) {
    // Register handlers for SIGHUP using one signal set.
    ASSERT_NO_THROW(signal_set_.reset(new SignalSet(SIGHUP)));
    // Define another signal set and register a different signal.
    ASSERT_NO_THROW(secondary_signal_set_.reset(new SignalSet(SIGINT)));
    // The SIGHUP has been already registsred with the other signal
    // set, so it should not be possible to register it again.
    ASSERT_THROW(secondary_signal_set_->add(SIGHUP), SignalSetError);
    // It shouldn't be possible to remove the signal registered in a different
    // signal set.
    ASSERT_THROW(secondary_signal_set_->remove(SIGHUP), SignalSetError);
    // Remove all signals from the first signal set. The signals registered
    // with the other signal signal set should be preserved.
    ASSERT_NO_THROW(signal_set_->clear());
    // Check indirectly that the SIGINT is still registered. An attempt to
    // register registered signal should result in failure.
    EXPECT_THROW(secondary_signal_set_->add(SIGINT), SignalSetError);
    // But, we should be able to regsiter SIGHUP.
    EXPECT_NO_THROW(secondary_signal_set_->add(SIGHUP));
}

/// Check that it is not possible to duplicate signals.
TEST_F(SignalSetTest, duplicates) {
    ASSERT_NO_THROW(signal_set_.reset(new SignalSet(SIGHUP)));
    // It shouldn't be possible to register the same signal.
    EXPECT_THROW(signal_set_->add(SIGHUP), SignalSetError);
    // But ok to register a different one.
    EXPECT_NO_THROW(signal_set_->add(SIGTERM));
    // Now, let's define other signal set.
    SignalSetPtr other;
    // SIGINT hasn't been registered in the first signal set
    // so it should be fine to register.
    ASSERT_NO_THROW(other.reset(new SignalSet(SIGINT)));
    // SIGHUP has been already registered in the first signal set so
    // an attempt to register it again should result in a failure.
    EXPECT_THROW(other->add(SIGHUP), SignalSetError);
}


} // end of anonymous namespace