diff options
Diffstat (limited to 'src/lib/util/tests/stopwatch_unittest.cc')
-rw-r--r-- | src/lib/util/tests/stopwatch_unittest.cc | 307 |
1 files changed, 307 insertions, 0 deletions
diff --git a/src/lib/util/tests/stopwatch_unittest.cc b/src/lib/util/tests/stopwatch_unittest.cc new file mode 100644 index 0000000..a506b6b --- /dev/null +++ b/src/lib/util/tests/stopwatch_unittest.cc @@ -0,0 +1,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 |