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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
|
// Copyright (C) 2015-2021 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <config.h>
#include <util/pid_file.h>
#include <gtest/gtest.h>
#include <fstream>
#include <signal.h>
#include <stdint.h>
namespace {
using namespace isc::util;
// Filenames used for testing.
const char* TESTNAME = "pid_file.test";
class PIDFileTest : public ::testing::Test {
public:
/// @brief Constructor
PIDFileTest() = default;
/// @brief Destructor
virtual ~PIDFileTest() = default;
/// @brief Prepends the absolute path to the file specified
/// as an argument.
///
/// @param filename Name of the file.
/// @return Absolute path to the test file.
static std::string absolutePath(const std::string& filename);
/// @brief Generate a random number for use as a PID
///
/// @param start - the start of the range we want the PID in
/// @param range - the size of the range for our PID
///
/// @return returns a random value between start and start + range
int randomizePID(const uint32_t start, const uint32_t range) {
int pid;
for (pid = (random() % range) + start;
kill(pid, 0) == 0;
++pid)
;
return (pid);
}
protected:
/// @brief Removes any old test files before the test
virtual void SetUp() {
removeTestFile();
}
/// @brief Removes any remaining test files after the test
virtual void TearDown() {
removeTestFile();
}
private:
/// @brief Removes any remaining test files
void removeTestFile() const {
static_cast<void>(remove(absolutePath(TESTNAME).c_str()));
}
};
std::string
PIDFileTest::absolutePath(const std::string& filename) {
std::ostringstream s;
s << TEST_DATA_BUILDDIR << "/" << filename;
return (s.str());
}
/// @brief Test file writing and deletion. Start by removing
/// any leftover file. Then write a known PID to the file and
/// attempt to read the file and verify the PID. Next write
/// a second and verify a second PID to verify that an existing
/// file is properly overwritten.
TEST_F(PIDFileTest, writeAndDelete) {
PIDFile pid_file(absolutePath(TESTNAME));
std::ifstream fs;
int pid(0);
// Write a known process id
pid_file.write(10);
// Read the file and compare the pid
fs.open(absolutePath(TESTNAME).c_str(), std::ifstream::in);
fs >> pid;
EXPECT_TRUE(fs.good());
EXPECT_EQ(pid, 10);
fs.close();
// Write a second known process id
pid_file.write(20);
// And compare the second pid
fs.open(absolutePath(TESTNAME).c_str(), std::ifstream::in);
fs >> pid;
EXPECT_TRUE(fs.good());
EXPECT_EQ(pid, 20);
fs.close();
// Delete the file
pid_file.deleteFile();
// And verify that it's gone
fs.open(absolutePath(TESTNAME).c_str(), std::ifstream::in);
EXPECT_FALSE(fs.good());
fs.close();
}
/// @brief Test checking a PID. Write the PID of the current
/// process to the PID file then verify that check indicates
/// the process is running.
TEST_F(PIDFileTest, pidInUse) {
PIDFile pid_file(absolutePath(TESTNAME));
// Write the current PID
pid_file.write();
// Check if we think the process is running
EXPECT_EQ(getpid(), pid_file.check());
}
/// @brief Test checking a PID. Write a PID that isn't in use
/// to the PID file and verify that check indicates the process
/// isn't running. The PID may get used between when we select it
/// and write the file and when we check it. To minimize false
/// errors if the first call to check fails we try again with a
/// different range of values and only if both attempts fail do
/// we declare the test to have failed.
TEST_F(PIDFileTest, pidNotInUse) {
PIDFile pid_file(absolutePath(TESTNAME));
int pid;
// get a pid between 10000 and 20000
pid = randomizePID(10000, 10000);
// write it
pid_file.write(pid);
// Check to see if we think the process is running
if (pid_file.check() == 0) {
return;
}
// get a pid between 40000 and 50000
pid = randomizePID(10000, 40000);
// write it
pid_file.write(pid);
// Check to see if we think the process is running
EXPECT_EQ(0, pid_file.check());
}
/// @brief Test checking a PID. Write garbage to the PID file
/// and verify that check throws an error. In this situation
/// the caller should probably log an error and may decide to
/// continue or not depending on the requirements.
TEST_F(PIDFileTest, pidGarbage) {
PIDFile pid_file(absolutePath(TESTNAME));
std::ofstream fs;
// Open the file and write garbage to it
fs.open(absolutePath(TESTNAME).c_str(), std::ofstream::out);
fs << "text" << std::endl;
fs.close();
// Run the check, we expect to get an exception
EXPECT_THROW(pid_file.check(), PIDCantReadPID);
}
/// @brief Test failing to write a file.
TEST_F(PIDFileTest, pidWriteFail) {
PIDFile pid_file(absolutePath(TESTNAME));
// Create the test file and change it's permission bits
// so we can't write to it.
pid_file.write(10);
chmod(absolutePath(TESTNAME).c_str(), S_IRUSR);
// Now try a write to the file, expecting an exception
EXPECT_THROW(pid_file.write(10), PIDFileError);
// Don't forget to restore the write right for the next test
chmod(absolutePath(TESTNAME).c_str(), S_IRUSR | S_IWUSR);
}
/// @brief Test deleting a file that doesn't exist
TEST_F(PIDFileTest, noDeleteFile) {
PIDFile pid_file(absolutePath(TESTNAME));
// Delete a file we haven't created
pid_file.deleteFile();
}
} // end of anonymous namespace
|