From: Marcin Siodelski Date: Fri, 17 Apr 2015 13:44:47 +0000 (+0200) Subject: [3804] Created Stopwatch class handling code execution time. X-Git-Tag: trac3791_base~2^2~5 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=600e6b770e1f7721f9c6bf1eec754a76fb7a4517;p=thirdparty%2Fkea.git [3804] Created Stopwatch class handling code execution time. --- diff --git a/src/lib/util/Makefile.am b/src/lib/util/Makefile.am index 97f2467968..9e722f0478 100644 --- a/src/lib/util/Makefile.am +++ b/src/lib/util/Makefile.am @@ -22,6 +22,8 @@ libkea_util_la_SOURCES += pid_file.h pid_file.cc libkea_util_la_SOURCES += process_spawn.h process_spawn.cc libkea_util_la_SOURCES += range_utilities.h libkea_util_la_SOURCES += signal_set.cc signal_set.h +libkea_util_la_SOURCES += stopwatch.cc stopwatch.h +libkea_util_la_SOURCES += stopwatch_impl.cc stopwatch_impl.h libkea_util_la_SOURCES += encode/base16_from_binary.h libkea_util_la_SOURCES += encode/base32hex.h encode/base64.h libkea_util_la_SOURCES += encode/base32hex_from_binary.h diff --git a/src/lib/util/stopwatch.cc b/src/lib/util/stopwatch.cc new file mode 100644 index 0000000000..b302470401 --- /dev/null +++ b/src/lib/util/stopwatch.cc @@ -0,0 +1,77 @@ +// Copyright (C) 2015 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 +#include + +namespace isc { +namespace util { + +using namespace boost::posix_time; + +Stopwatch::Stopwatch(const bool autostart) + : impl_(new StopwatchImpl()) { + // If the autostart has been specified, invoke start. + if (autostart) { + start(); + } +} + +void +Stopwatch::start() { + impl_->start(); +} + +void +Stopwatch::stop() { + impl_->stop(); +} + +void +Stopwatch::reset() { + impl_->reset(); +} + +boost::posix_time::time_duration +Stopwatch::getLastDuration() const { + return (impl_->getLastDuration()); +} + +boost::posix_time::time_duration +Stopwatch::getTotalDuration() const { + return (impl_->getTotalDuration()); +} + +long +Stopwatch::getMilliseconds() const { + return (getLastDuration().total_milliseconds()); +} + +long +Stopwatch::getTotalMilliseconds() const { + return (getTotalDuration().total_milliseconds()); +} + +long +Stopwatch::getMicroseconds() const { + return (getLastDuration().total_microseconds()); +} + +long +Stopwatch::getTotalMicroseconds() const { + return (getTotalDuration().total_microseconds()); +} + +} // end of isc::util +} // end of isc diff --git a/src/lib/util/stopwatch.h b/src/lib/util/stopwatch.h new file mode 100644 index 0000000000..fdd693f887 --- /dev/null +++ b/src/lib/util/stopwatch.h @@ -0,0 +1,124 @@ +// Copyright (C) 2015 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. + +#ifndef STOPWATCH_H +#define STOPWATCH_H + +#include +#include + +namespace isc { +namespace util { + +/// @brief Forward declaration to the @c Stopwatch implementation. +class StopwatchImpl; + +/// @brief Utility class to measure code execution times. +/// +/// The API of this class is based on the use cases of a stopwatch. It is +/// used to measure time spent executing portions of the code. The typical +/// use case for the @c Stopwatch is to measure the time spent invoking +/// callouts in hooks library. This provides means for diagnosing the +/// server's performance degradations when hooks libraries are in use. +/// +/// This class exposes functions like @c start, @c stop and @c reset which +/// behave in the same way as a stopwatch used to measure time for sport +/// activities. +/// +/// It is possible to measure the cumulative execution time by invoking +/// @c start and @c stop consecutively. The total measured time will be +/// a sum of durations between the invocations of respective starts and +/// stops. +class Stopwatch { +public: + + /// @brief Constructor. + /// + /// @param autostart Indicates if the stopwatch should be initialized to + /// the "started" state. In this state the stopwatch is measuring the time + /// since it has been started (object has been constructed in this case. + /// If the parameter is set to false (default value), the + /// @c Stopwatch::start must be called to start time measurement. + Stopwatch(const bool autostart = true); + + /// @brief Starts the stopwatch. + /// + /// Sets the stopwatch to the "started" state. In this state the stopwatch + /// is measuring the duration since @c Stopwatch::start has been invoked. + /// + //// This method is no-op if the stopwatch is already in the "stopped" + /// state. + void start(); + + /// @brief Stops the stopwatch. + /// + /// Sets the stopwatch to the "stopped" state. The stopwatch stops the time + /// measurement and records the duration between the last stopwatch start + /// and the stop invocation. It also updates the total measured duration, + /// i.e. the sum of durations between all start/stop invocations. Both + /// values can be retrieved using @c Stopwatch::getLastDuration and + /// @c Stopwatch::getTotalDuration respectively, or their variants. + /// + /// This method is no-op if the stopwatch is already in the "stopped" state. + void stop(); + + /// @brief Resets the stopwatch. + /// + /// It resets the stopwatch to the initial state. In this state, the last + /// measured duration and the total duration is set to 0. The stopwatch + /// is set to the "stopped" state. + void reset(); + + /// @brief Retrieves last measured duration. + /// + /// If the stopwatch is in the "stopped" state this method retrieves the + /// duration between the last start and stop. If the stopwatch is in the + /// "started" state, the retrieved duration is the duration between the + /// last start of the stopwatch and the current time. + boost::posix_time::time_duration getLastDuration() const; + + /// @brief Retrieves total measured duration. + /// + /// If the stopwatch is in the "stopped" state this method retrieves the + /// total duration between all starts and stops invoked during the + /// lifetime of the object or since the last reset. If the stopwatch is + /// in the "started" state, the returned is the sum of all durations + /// between respective starts and stops, and the duration since the + /// stopwatch has been last started and the current time. + boost::posix_time::time_duration getTotalDuration() const; + + /// @brief Retrieves the last measured duration in milliseconds. + long getMilliseconds() const; + + /// @brief Retrieves the total measured duration in milliseconds. + long getTotalMilliseconds() const; + + /// @brief Retrieves the last measured duration in microseconds. + long getMicroseconds() const; + + /// @brief Retrieves the total measured duration in microseconds. + long getTotalMicroseconds() const; + +private: + + /// @brief Pointer to the @c StopwatchImpl. + boost::scoped_ptr impl_; + +}; + +} +} + +#endif // STOPWATCH_H + diff --git a/src/lib/util/stopwatch_impl.cc b/src/lib/util/stopwatch_impl.cc new file mode 100644 index 0000000000..e3106d6f33 --- /dev/null +++ b/src/lib/util/stopwatch_impl.cc @@ -0,0 +1,92 @@ +// Copyright (C) 2015 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 + +namespace isc { +namespace util { + +using namespace boost::posix_time; + +StopwatchImpl::StopwatchImpl() + : started_(false), + last_start_(getCurrentTime()), + last_stop_(last_start_), + cumulative_time_(microseconds(0)) { +} + +StopwatchImpl::~StopwatchImpl() { +} + +void +StopwatchImpl::start() { + // If stopwatch is "stopped", start it. + if (!started_) { + last_start_ = getCurrentTime(); + started_ = true; + } +} + +void +StopwatchImpl::stop() { + // Is stopwatch is "started", stop it. + if (started_) { + last_stop_ = getCurrentTime(); + // Update the total time with the last measured duration. + cumulative_time_ += last_stop_ - last_start_; + started_ = false; + } +} + +void +StopwatchImpl::reset() { + // Set last start and stop values to the current time. This is the + // same as in the constructor. As a result the last duration will + // be 0. + last_start_ = getCurrentTime(); + last_stop_ = last_start_; + // Set the total duration to 0. + cumulative_time_ = microseconds(0); + started_ = false; +} + +time_duration +StopwatchImpl::getLastDuration() const { + // If the stopwatch is started, the time measured is between the + // start time and the current time. Otherwise, it is between the + // start time and last stop. + ptime end_time = started_ ? getCurrentTime() : last_stop_; + return (end_time - last_start_); +} + +time_duration +StopwatchImpl::getTotalDuration() const { + // Get the total time recorded so far. + time_duration total_duration = cumulative_time_; + if (started_) { + // If the stopwatch is started, add the duration between the + // start time and current time. + total_duration += (getCurrentTime() - last_start_); + } + return (total_duration); +} + +ptime +StopwatchImpl::getCurrentTime() const { + return (microsec_clock::universal_time()); +} + + +} // end of isc::util +} // end of isc diff --git a/src/lib/util/stopwatch_impl.h b/src/lib/util/stopwatch_impl.h new file mode 100644 index 0000000000..7d440bf20f --- /dev/null +++ b/src/lib/util/stopwatch_impl.h @@ -0,0 +1,121 @@ +// Copyright (C) 2015 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. + +#ifndef STOPWATCH_IMPL_H +#define STOPWATCH_IMPL_H + +#include +#include + +namespace isc { +namespace util { + +/// @brief @c Stopwatch class implementation. +/// +/// The @c Stopwatch class uses the plimpl idiom to make it easier to unit +/// test behavior of the @c Stopwatch class without a need to rely on the system +/// clock. The @c StopwatchImpl API allows for overriding the @c getCurrentTime +/// method to return the arbitrary time value as current time to various +/// methods. By setting the current time to arbitrary values the test can expect +/// arbitrary values being returned by the class methods. +/// +/// Also, by using the pimpl idiom the @c Stopwatch class hides its implementation +/// details and leaves the header with only the pointer to the @c StopwatchImpl +/// class. +class StopwatchImpl { +public: + + /// @brief Constructor. + /// + /// Initializes the internally used timestamps. It also sets the state of + /// the stopwatch to "stopped". + StopwatchImpl(); + + /// @brief Virtual destructor. + /// + /// This destructor is virtual because the @c StopwatchImpl::getCurrentTime + /// is virtual. + virtual ~StopwatchImpl(); + + /// @brief Starts the stopwatch. + /// + /// Sets the stopwatch to the "started" state. It also records the time when + /// the stopwatch is started. This method is no-op if the stopwatch is + /// already in the "started" state. + /// + /// Also see the @c Stopwatch::start for details. + void start(); + + /// @brief Stop the stopwatch. + /// + /// Sets the stopwatch to the "stopped" state. The stop time is recorded and + /// the cumulative time is updated to include the duration between the most + /// recent start and stop. This method is no-op if the stopwatch is already + /// in the "stopped" state. + /// + /// Also see the @c Stopwatch::stop for details. + void stop(); + + /// @brief Reset the stopwatch. + /// + /// Also see the @c Stopwatch::reset for details. + void reset(); + + /// @brief Retrieves the measured duration. + /// + /// Also see the @c Stopwatch::getLastDuration for details. + boost::posix_time::time_duration getLastDuration() const; + + /// @brief Retrieves the total measured duration. + /// + /// Also see the @c Stopwatch::getTotalDuration for details. + boost::posix_time::time_duration getTotalDuration() const; + +protected: + + /// @brief Returns the current time. + /// + /// This method is used internally by the @c StopwatchImpl class and + /// its derivations. This class simply returns the value of + /// @c boost::posix_time::micrisec_clock::univeral_time(), which is + /// a current timestamp. The derivations may replace it with the + /// custom implementations. The typical use case is for the unit tests + /// to customize the behavior of this function to return well known + /// (deterministic) values. As a result, it is possible to influence + /// the "measured" values returned by accessors of this class, which + /// can be compared against some exact values. + virtual boost::posix_time::ptime getCurrentTime() const; + +private: + + /// @brief Holds the state of the stopwatch. + bool started_; + + /// @brief Holds the timestamp when the stopwatch has been last started. + boost::posix_time::ptime last_start_; + + /// @brief Holds the timestamp when the stopwatch has been last stopped. + boost::posix_time::ptime last_stop_; + + /// @brief Holds the total measured time since the stopwatch has been + /// first started after creation or reset. + boost::posix_time::time_duration cumulative_time_; + +}; + +} +} + +#endif // STOPWATCH_H + diff --git a/src/lib/util/tests/Makefile.am b/src/lib/util/tests/Makefile.am index 51d5f78f41..9d727cb88a 100644 --- a/src/lib/util/tests/Makefile.am +++ b/src/lib/util/tests/Makefile.am @@ -49,6 +49,7 @@ run_unittests_SOURCES += strutil_unittest.cc run_unittests_SOURCES += time_utilities_unittest.cc run_unittests_SOURCES += range_utilities_unittest.cc run_unittests_SOURCES += signal_set_unittest.cc +run_unittests_SOURCES += stopwatch_unittest.cc run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) diff --git a/src/lib/util/tests/stopwatch_unittest.cc b/src/lib/util/tests/stopwatch_unittest.cc new file mode 100644 index 0000000000..361a7af2ef --- /dev/null +++ b/src/lib/util/tests/stopwatch_unittest.cc @@ -0,0 +1,283 @@ +// Copyright (C) 2015 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 +#include +#include +#include +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. 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. + void ffwd(const uint32_t ms); + + /// @brief Returns the last duration in milliseconds. + uint32_t getLastDurationInMs() const; + + /// @brief Returns the total duration in milliseconds. + uint32_t getTotalDurationInMs() const; + +protected: + + /// @brief Returs 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) { + current_time_ += milliseconds(ms); +} + +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 { +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.getMilliseconds()); + 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.getMilliseconds(); + 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.getMilliseconds()); + EXPECT_EQ(current_duration, stopwatch.getTotalMilliseconds()); + + // Start it again. + stopwatch.start(); + + // Slee for 1 ms. + usleep(1000); + + // The durations should get updated as appropriate. + current_duration = stopwatch.getMilliseconds(); + EXPECT_GE(stopwatch.getMilliseconds(), 1); + EXPECT_GE(stopwatch.getTotalMilliseconds(), 2); +} + +// Make sure that we can obtain the durations as microseconds. +TEST_F(StopwatchTest, getMicroseconds) { + Stopwatch stopwatch; + stopwatch.start(); + + usleep(1000); + + stopwatch.stop(); + + long current_duration = stopwatch.getMicroseconds(); + 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.getMilliseconds(), 1); + EXPECT_EQ(stopwatch.getMilliseconds(), stopwatch.getTotalMilliseconds()); +} + +} // end of anonymous namespace