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
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
|
// Copyright (C) 2015-2023 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/stopwatch.h>
#include <util/stopwatch_impl.h>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <gtest/gtest.h>
#include <unistd.h>
namespace {
using namespace isc;
using namespace isc::util;
using namespace boost::posix_time;
/// @brief @c StopwatchImpl mock object.
///
/// This class derives from the @c StopwatchImpl to override the
/// @c StopwatchImpl::getCurrentTime. This method is internally called by
/// the @c StopwatchImpl to determine the current time. By providing the
/// implementation of this method which returns the fixed (well known)
/// timestamp value we can obtain the deterministic values from the accessors
/// of this class.
///
/// This class also includes some convenience methods to return the time
/// durations in milliseconds.
class StopwatchMock : public StopwatchImpl {
public:
/// @brief Constructor.
///
/// @param ref_time Reference time, i.e. the arbitrary time value from
/// which time is measured. The @c current_time_ value returned by the
/// @c StopwatchMock::getCurrentTime is initialized to this value.
/// Subsequent calls to the @c StopwatchMock::ffwd move the value of
/// the @c current_time_ forward.
StopwatchMock(const ptime& ref_time);
/// @brief Fast forward time.
///
/// Moves the value of the @c current_time_ forward by the specified
/// number of milliseconds (microseconds). As a result the timestamp
/// returned by the @c StopwatchMock::getCurrentTime moves by this value.
/// This simulates the time progress.
///
/// @param ms Specifies the number of milliseconds to move current time.
/// @param us Specifies the number of fractional microseconds to move
/// current time.
void ffwd(const uint32_t ms, const uint32_t us = 0);
/// @brief Returns the last duration in milliseconds.
uint32_t getLastDurationInMs() const;
/// @brief Returns the total duration in milliseconds.
uint32_t getTotalDurationInMs() const;
protected:
/// @brief Returns the current time.
///
/// This method returns the fixed @c current_time_ timestamp.
virtual ptime getCurrentTime() const;
private:
/// @brief Holds the current time to be returned by the
/// @c StopwatchMock::getCurrentTime.
ptime current_time_;
};
StopwatchMock::StopwatchMock(const ptime& ref_time)
: StopwatchImpl(), current_time_(ref_time) {
}
void
StopwatchMock::ffwd(const uint32_t ms, const uint32_t us) {
current_time_ += milliseconds(ms);
current_time_ += microseconds(us);
}
uint32_t
StopwatchMock::getLastDurationInMs() const {
return (getLastDuration().total_milliseconds());
}
uint32_t
StopwatchMock::getTotalDurationInMs() const {
return (getTotalDuration().total_milliseconds());
}
ptime
StopwatchMock::getCurrentTime() const {
return (current_time_);
}
/// @brief Test fixture class for testing @c StopwatchImpl.
class StopwatchTest : public ::testing::Test {
public:
/// @brief Constructor
StopwatchTest() = default;
/// @brief Destructor
virtual ~StopwatchTest() = default;
protected:
/// @brief Set up the test.
///
/// Initializes the reference time to be used to create the instances
/// of the @c StopwatchMock objects.
virtual void SetUp();
/// @brief Holds the reference time to be used to create the instances
/// of the @c StopwatchMock objects.
ptime ref_time_;
};
void
StopwatchTest::SetUp() {
ref_time_ = microsec_clock::universal_time();
}
/// This test checks the behavior of the stopwatch when it is started
/// and stopped multiple times. It uses the StopwatchMock object to
/// control the "time flow" by setting the current time to arbitrary
/// values using the StopwatchMock::ffwd. In addition, this test
/// checks that the stopwatch can be reset.
TEST_F(StopwatchTest, multipleMeasurements) {
StopwatchMock stopwatch(ref_time_);
// The stopwatch shouldn't automatically start. The initial
// durations should be set to 0.
EXPECT_EQ(0, stopwatch.getLastDurationInMs());
EXPECT_EQ(0, stopwatch.getTotalDurationInMs());
stopwatch.start();
// Even though the stopwatch is started, the time is still set to
// the initial value. The durations should not be affected.
EXPECT_EQ(0, stopwatch.getLastDurationInMs());
EXPECT_EQ(0, stopwatch.getTotalDurationInMs());
// Move the time by 10 ms.
stopwatch.ffwd(10);
// It should be possible to retrieve the durations even when the
// stopwatch is running.
EXPECT_EQ(10, stopwatch.getLastDurationInMs());
EXPECT_EQ(10, stopwatch.getTotalDurationInMs());
// Now stop it and make sure that the same values are returned.
stopwatch.stop();
EXPECT_EQ(10, stopwatch.getLastDurationInMs());
EXPECT_EQ(10, stopwatch.getTotalDurationInMs());
// Start it again, but don't move the time forward yet.
stopwatch.start();
// The new duration should be 0, but the total should be equal to
// the previously measured duration.
EXPECT_EQ(0, stopwatch.getLastDurationInMs());
EXPECT_EQ(10, stopwatch.getTotalDurationInMs());
// Move time by 5 ms.
stopwatch.ffwd(5);
// New measured duration should be 5 ms. The total should be 15 ms.
EXPECT_EQ(5, stopwatch.getLastDurationInMs());
EXPECT_EQ(15, stopwatch.getTotalDurationInMs());
// Stop it again and make sure the values returned are the same.
stopwatch.stop();
EXPECT_EQ(5, stopwatch.getLastDurationInMs());
EXPECT_EQ(15, stopwatch.getTotalDurationInMs());
// Move the time forward while the stopwatch is stopped.
stopwatch.ffwd(8);
// The measured values should not be affected.
EXPECT_EQ(5, stopwatch.getLastDurationInMs());
EXPECT_EQ(15, stopwatch.getTotalDurationInMs());
// Stop should be no-op in this case.
stopwatch.stop();
EXPECT_EQ(5, stopwatch.getLastDurationInMs());
EXPECT_EQ(15, stopwatch.getTotalDurationInMs());
// Start the stopwatch again.
stopwatch.start();
// Move time by 3 ms.
stopwatch.ffwd(3);
// Since the stopwatch is running, the measured duration should
// get updated again.
EXPECT_EQ(3, stopwatch.getLastDurationInMs());
EXPECT_EQ(18, stopwatch.getTotalDurationInMs());
// Move the time by 2 ms.
stopwatch.ffwd(2);
// Start should be no-op in this case.
stopwatch.start();
// But the durations should be updated.
EXPECT_EQ(5, stopwatch.getLastDurationInMs());
EXPECT_EQ(20, stopwatch.getTotalDurationInMs());
// Make sure we can reset.
stopwatch.reset();
EXPECT_EQ(0, stopwatch.getLastDurationInMs());
EXPECT_EQ(0, stopwatch.getTotalDurationInMs());
}
// This test checks that the stopwatch works when the real clock is in use.
TEST_F(StopwatchTest, realTime) {
// Initially, the measured time should be 0.
Stopwatch stopwatch;
EXPECT_EQ(0, stopwatch.getLastMilliseconds());
EXPECT_EQ(0, stopwatch.getTotalMilliseconds());
// Start the stopwatch.
stopwatch.start();
// Sleep for 1 ms. The stopwatch should measure this duration.
usleep(1000);
stopwatch.stop();
// The measured duration should be greater or equal 1 ms.
long current_duration = stopwatch.getLastMilliseconds();
EXPECT_GE(current_duration, 1);
EXPECT_EQ(current_duration, stopwatch.getTotalMilliseconds());
// Sleep for another 2 ms while the stopwatch is in the stopped state.
usleep(2000);
// In the stopped state, we should still have old durations measured.
EXPECT_EQ(current_duration, stopwatch.getLastMilliseconds());
EXPECT_EQ(current_duration, stopwatch.getTotalMilliseconds());
// Start it again.
stopwatch.start();
// Sleep for 1 ms.
usleep(1000);
// The durations should get updated as appropriate.
EXPECT_GE(stopwatch.getLastMilliseconds(), 1);
EXPECT_GE(stopwatch.getTotalMilliseconds(), 2);
}
// Make sure that we can obtain the durations as microseconds.
TEST_F(StopwatchTest, getLastMicroseconds) {
Stopwatch stopwatch;
stopwatch.start();
usleep(1000);
stopwatch.stop();
long current_duration = stopwatch.getLastMicroseconds();
EXPECT_GE(current_duration, 1000);
EXPECT_EQ(current_duration, stopwatch.getTotalMicroseconds());
}
// Make sure that we can use the "autostart" option to start the time
// measurement in the constructor.
TEST_F(StopwatchTest, autostart) {
Stopwatch stopwatch(true);
usleep(1000);
stopwatch.stop();
EXPECT_GE(stopwatch.getLastMilliseconds(), 1);
EXPECT_EQ(stopwatch.getLastMilliseconds(), stopwatch.getTotalMilliseconds());
}
// Make sure that the conversion to the loggable string works as expected.
TEST_F(StopwatchTest, logFormat) {
time_duration duration = microseconds(223043);
EXPECT_EQ("223.043 ms", StopwatchImpl::logFormat(duration));
duration = microseconds(1234);
EXPECT_EQ("1.234 ms", StopwatchImpl::logFormat(duration));
duration = microseconds(2000);
EXPECT_EQ("2.000 ms", StopwatchImpl::logFormat(duration));
duration = milliseconds(2100);
EXPECT_EQ("2.10 s", StopwatchImpl::logFormat(duration));
duration = milliseconds(3123);
EXPECT_EQ("3.12 s", StopwatchImpl::logFormat(duration));
}
} // end of anonymous namespace
|