Browse Source

[3793] Changes after review:
- {ptime,duration}ToText moved to lib/util
- missing comments added
- EXPECT_EQ -> ASSERT_EQ
- several consts added
- protected methods are now private in Observation
- tabs converted to spaces

Tomek Mrugalski 10 years ago
parent
commit
b0a36bccee

+ 2 - 2
configure.ac

@@ -1494,8 +1494,8 @@ AC_CONFIG_FILES([compatcheck/Makefile
                  src/lib/testutils/Makefile
                  src/lib/testutils/dhcp_test_lib.sh
                  src/lib/testutils/testdata/Makefile
-		 src/lib/stats/Makefile
-		 src/lib/stats/tests/Makefile
+                 src/lib/stats/Makefile
+                 src/lib/stats/tests/Makefile
                  src/lib/util/Makefile
                  src/lib/util/io/Makefile
                  src/lib/util/python/Makefile

+ 0 - 1
src/lib/cc/data.h

@@ -677,7 +677,6 @@ public:
 
     /// @brief Returns number of stored elements
     ///
-    /// @note: this is costly operation for a map!
     /// @return number of elements.
     size_t size() const {
         return (m.size());

+ 3 - 6
src/lib/stats/Makefile.am

@@ -1,6 +1,7 @@
 SUBDIRS = . tests
 
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
 AM_CXXFLAGS=$(KEA_CXXFLAGS)
 
 lib_LTLIBRARIES = libkea-stats.la
@@ -8,13 +9,9 @@ libkea_stats_la_SOURCES = observation.h observation.cc
 libkea_stats_la_SOURCES += context.h context.cc
 libkea_stats_la_SOURCES += stats_mgr.h stats_mgr.cc
 
+libkea_stats_la_CPPFLAGS = $(AM_CPPFLAGS)
 libkea_stats_la_LIBADD  = $(top_builddir)/src/lib/cc/libkea-cc.la
-
-# This library seem to be required when certain methods are called from
-# boost::posix_time. In particular, to_simple_string(ptime) and
-# to_simple_string(time_duration) require it on Ubuntu. It doesn't seem
-# to be required on Mac OS, though.
-libkea_stats_la_LIBADD += -lboost_date_time
+libkea_stats_la_LIBADD += $(top_builddir)/src/lib/util/libkea-util.la
 
 libkea_stats_includedir = $(includedir)/$(PACKAGE_NAME)/stats
 libkea_stats_include_HEADERS = stats_mgr.h

+ 35 - 31
src/lib/stats/observation.cc

@@ -1,6 +1,22 @@
-#include <boost/date_time/posix_time/posix_time.hpp>
+// 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 <stats/observation.h>
+#include <util/boost_time_utils.h>
 #include <cc/data.h>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/date_time/gregorian/gregorian.hpp>
 #include <utility>
 
 using namespace std;
@@ -10,17 +26,17 @@ using namespace boost::posix_time;
 namespace isc {
 namespace stats {
 
-Observation::Observation(const std::string& name, uint64_t value)
+Observation::Observation(const std::string& name, const uint64_t value)
     :name_(name), type_(STAT_INTEGER) {
     setValue(value);
 }
 
-Observation::Observation(const std::string& name, double value)
+Observation::Observation(const std::string& name, const double value)
     :name_(name), type_(STAT_FLOAT) {
     setValue(value);
 }
 
-Observation::Observation(const std::string& name, StatsDuration value)
+Observation::Observation(const std::string& name, const StatsDuration value)
     :name_(name), type_(STAT_DURATION) {
     setValue(value);
 }
@@ -30,17 +46,17 @@ Observation::Observation(const std::string& name, const std::string& value)
     setValue(value);
 }
 
-void Observation::addValue(uint64_t value) {
+void Observation::addValue(const uint64_t value) {
     IntegerSample current = getInteger();
     setValue(current.first + value);
 }
 
-void Observation::addValue(double value) {
+void Observation::addValue(const double value) {
     FloatSample current = getFloat();
     setValue(current.first + value);
 }
 
-void Observation::addValue(StatsDuration value) {
+void Observation::addValue(const StatsDuration& value) {
     DurationSample current = getDuration();
     setValue(current.first + value);
 }
@@ -50,15 +66,15 @@ void Observation::addValue(const std::string& value) {
     setValue(current.first + value);
 }
 
-void Observation::setValue(uint64_t value) {
+void Observation::setValue(const uint64_t value) {
     setValueInternal(value, integer_samples_, STAT_INTEGER);
 }
 
-void Observation::setValue(double value) {
+void Observation::setValue(const double value) {
     setValueInternal(value, float_samples_, STAT_FLOAT);
 }
 
-void Observation::setValue(StatsDuration value) {
+void Observation::setValue(const StatsDuration& value) {
     setValueInternal(value, duration_samples_, STAT_DURATION);
 }
 
@@ -140,20 +156,6 @@ std::string Observation::typeToText(Type type) {
     return (tmp.str());
 }
 
-std::string
-Observation::ptimeToText(ptime t) {
-
-    // The alternative would be to call to_simple_string(ptime), but unfortunately
-    // that requires linking with boost libraries.
-
-    return (to_simple_string(t));
-}
-
-std::string
-Observation::durationToText(StatsDuration dur) {
-    return (to_simple_string(dur));
-}
-
 isc::data::ConstElementPtr
 Observation::getJSON() const {
 
@@ -168,29 +170,30 @@ Observation::getJSON() const {
     case STAT_INTEGER: {
         IntegerSample s = getInteger();
         value = isc::data::Element::create(static_cast<int64_t>(s.first));
-        timestamp = isc::data::Element::create(ptimeToText(s.second));
+        timestamp = isc::data::Element::create(isc::util::ptimeToText(s.second));
         break;
     }
     case STAT_FLOAT: {
         FloatSample s = getFloat();
         value = isc::data::Element::create(s.first);
-        timestamp = isc::data::Element::create(ptimeToText(s.second));
+        timestamp = isc::data::Element::create(isc::util::ptimeToText(s.second));
         break;
     }
     case STAT_DURATION: {
         DurationSample s = getDuration();
-        value = isc::data::Element::create(durationToText(s.first));
-        timestamp = isc::data::Element::create(ptimeToText(s.second));
+        value = isc::data::Element::create(isc::util::durationToText(s.first));
+        timestamp = isc::data::Element::create(isc::util::ptimeToText(s.second));
         break;
     }
     case STAT_STRING: {
         StringSample s = getString();
         value = isc::data::Element::create(s.first);
-        timestamp = isc::data::Element::create(ptimeToText(s.second));
+        timestamp = isc::data::Element::create(isc::util::ptimeToText(s.second));
         break;
     }
     default:
-        isc_throw(InvalidStatType, "Unknown stat type: " << typeToText(type_));
+        isc_throw(InvalidStatType, "Unknown statistic type: "
+                  << typeToText(type_));
     };
 
     entry->add(value);
@@ -221,7 +224,8 @@ void Observation::reset() {
         return;
     }
     default:
-        isc_throw(InvalidStatType, "Unknown stat type: " << typeToText(type_));
+        isc_throw(InvalidStatType, "Unknown statistic type: "
+                  << typeToText(type_));
     };
 }
 

+ 16 - 27
src/lib/stats/observation.h

@@ -15,12 +15,13 @@
 #ifndef OBSERVATION_H
 #define OBSERVATION_H
 
-#include <boost/shared_ptr.hpp>
-#include <exceptions/exceptions.h>
 #include <cc/data.h>
+#include <exceptions/exceptions.h>
+#include <boost/shared_ptr.hpp>
 #include <boost/date_time/time_duration.hpp>
 #include <boost/date_time/posix_time/posix_time_types.hpp>
 #include <list>
+#include <stdint.h>
 
 namespace isc {
 namespace stats {
@@ -37,11 +38,7 @@ public:
 
 /// @brief Defines duration resolution
 ///
-/// Boost offers a base boost::posix_time::time_duration class, that has specific
-/// implementations: boost::posix_time::{hours,minutes,seconds,millisec,microsec,
-/// nanosec}. For statistics purposes, the most appropriate choice seems to be
-/// microseconds precision, so we'll stick with that.
-typedef boost::posix_time::microsec::time_duration StatsDuration;
+typedef boost::posix_time::time_duration StatsDuration;
 
 /// @defgroup stat_samples Specifies supported observation types.
 ///
@@ -101,19 +98,19 @@ class Observation {
     ///
     /// @param name observation name
     /// @param value integer value observed.
-    Observation(const std::string& name, uint64_t value);
+    Observation(const std::string& name, const uint64_t value);
 
     /// @brief Constructor for floating point observations
     ///
     /// @param name observation name
     /// @param value floating point value observed.
-    Observation(const std::string& name, double value);
+    Observation(const std::string& name, const double value);
 
     /// @brief Constructor for duration observations
     ///
     /// @param name observation name
     /// @param value duration observed.
-    Observation(const std::string& name, StatsDuration value);
+    Observation(const std::string& name, const StatsDuration value);
 
     /// @brief Constructor for string observations
     ///
@@ -125,43 +122,43 @@ class Observation {
     ///
     /// @param value integer value observed
     /// @throw InvalidStatType if statistic is not integer
-    void setValue(uint64_t value);
+    void setValue(const uint64_t value);
 
     /// @brief Records absolute floating point observation
     ///
     /// @param value floating point value observed
     /// @throw InvalidStatType if statistic is not fp
-    void setValue(double value);
+    void setValue(const double value);
 
     /// @brief Records absolute duration observation
     ///
     /// @param value duration value observed
     /// @throw InvalidStatType if statistic is not time duration
-    void setValue(StatsDuration duration);
+    void setValue(const StatsDuration& duration);
 
     /// @brief Records absolute string observation
     ///
     /// @param value string value observed
     /// @throw InvalidStatType if statistic is not a string
-    void setValue(const std::string& value = "");
+    void setValue(const std::string& value);
 
     /// @brief Records incremental integer observation
     ///
     /// @param value integer value observed
     /// @throw InvalidStatType if statistic is not integer
-    void addValue(uint64_t value = 1);
+    void addValue(const uint64_t value = 1);
 
     /// @brief Records incremental floating point observation
     ///
     /// @param value floating point value observed
     /// @throw InvalidStatType if statistic is not fp
-    void addValue(double value = 1.0f);
+    void addValue(const double value = 1.0f);
 
     /// @brief Records incremental duration observation
     ///
     /// @param value duration value observed
     /// @throw InvalidStatType if statistic is not time duration
-    void addValue(StatsDuration value = StatsDuration(0,0,0,0));
+    void addValue(const StatsDuration& value = StatsDuration(0,0,1,0));
 
     /// @brief Records incremental string observation.
     ///
@@ -208,20 +205,12 @@ class Observation {
     /// @return textual name of statistic type
     static std::string typeToText(Type type);
 
-    /// @brief Converts ptime structure to text
-    /// @return a string representing time
-    static std::string ptimeToText(boost::posix_time::ptime time);
-
-    /// @brief Converts StatsDuration to text
-    /// @return a string representing time
-    static std::string durationToText(StatsDuration dur);
-
     /// @brief Returns observation name
     std::string getName() const {
         return (name_);
     }
 
- protected:
+private:
     /// @brief Records absolute sample (internal version)
     ///
     /// This method records an absolute value of an observation.
@@ -238,7 +227,7 @@ class Observation {
     void setValueInternal(SampleType value, StorageType& storage,
                           Type exp_type);
 
-    /// @brief Returns a sample
+    /// @brief Returns a sample (internal version)
     ///
     /// @tparam SampleType type of sample (e.g. IntegerSample)
     /// @tparam StorageType type of storage (e.g. list<IntegerSample>)

+ 10 - 10
src/lib/stats/stats_mgr.cc

@@ -30,30 +30,30 @@ StatsMgr::StatsMgr()
 
 }
 
-void StatsMgr::setValue(const std::string& name, uint64_t value) {
+void StatsMgr::setValue(const std::string& name, const uint64_t value) {
     setValueInternal(name, value);
 }
 
-void StatsMgr::setValue(const std::string& name, double value) {
+void StatsMgr::setValue(const std::string& name, const double value) {
     setValueInternal(name, value);
 }
 
-void StatsMgr::setValue(const std::string& name, StatsDuration value) {
+void StatsMgr::setValue(const std::string& name, const StatsDuration& value) {
     setValueInternal(name, value);
 }
 void StatsMgr::setValue(const std::string& name, const std::string& value) {
     setValueInternal(name, value);
 }
 
-void StatsMgr::addValue(const std::string& name, uint64_t value) {
+void StatsMgr::addValue(const std::string& name, const uint64_t value) {
     addValueInternal(name, value);
 }
 
-void StatsMgr::addValue(const std::string& name, double value) {
+void StatsMgr::addValue(const std::string& name, const double value) {
     addValueInternal(name, value);
 }
 
-void StatsMgr::addValue(const std::string& name, StatsDuration value) {
+void StatsMgr::addValue(const std::string& name, const StatsDuration& value) {
     addValueInternal(name, value);
 }
 
@@ -63,24 +63,24 @@ void StatsMgr::addValue(const std::string& name, const std::string& value) {
 
 ObservationPtr StatsMgr::getObservation(const std::string& name) const {
     /// @todo: Implement contexts.
-    // Currently we keep everyting is a global context.
+    // Currently we keep everyting in a global context.
     return (global_->get(name));
 }
 
 void StatsMgr::addObservation(const ObservationPtr& stat) {
     /// @todo: Implement contexts.
-    // Currently we keep everyting is a global context.
+    // Currently we keep everyting in a global context.
     return (global_->add(stat));
 }
 
 bool StatsMgr::deleteObservation(const std::string& name) {
     /// @todo: Implement contexts.
-    // Currently we keep everyting is a global context.
+    // Currently we keep everyting in a global context.
     return (global_->del(name));
 }
 
 void StatsMgr::setMaxSampleAge(const std::string& ,
-                               boost::posix_time::time_duration) {
+                               const StatsDuration&) {
     isc_throw(NotImplemented, "setMaxSampleAge not implemented");
 }
 

+ 31 - 29
src/lib/stats/stats_mgr.h

@@ -60,54 +60,54 @@ class StatsMgr : public boost::noncopyable {
     ///
     /// @{
 
-    /// @brief Records absolute integer observation
+    /// @brief Records absolute integer observation.
     ///
     /// @param name name of the observation
     /// @param value integer value observed
     /// @throw InvalidStatType if statistic is not integer
-    void setValue(const std::string& name, uint64_t value);
+    void setValue(const std::string& name, const uint64_t value);
 
-    /// @brief Records absolute floating point observation
+    /// @brief Records absolute floating point observation.
     ///
     /// @param name name of the observation
     /// @param value floating point value observed
     /// @throw InvalidStatType if statistic is not fp
-    void setValue(const std::string& name, double value);
+    void setValue(const std::string& name, const double value);
 
-    /// @brief Records absolute duration observation
+    /// @brief Records absolute duration observation.
     ///
     /// @param name name of the observation
     /// @param value duration value observed
     /// @throw InvalidStatType if statistic is not time duration
-    void setValue(const std::string& name, StatsDuration value);
+    void setValue(const std::string& name, const StatsDuration& value);
 
-    /// @brief Records absolute string observation
+    /// @brief Records absolute string observation.
     ///
     /// @param name name of the observation
     /// @param value string value observed
     /// @throw InvalidStatType if statistic is not a string
     void setValue(const std::string& name, const std::string& value);
 
-    /// @brief Records incremental integer observation
+    /// @brief Records incremental integer observation.
     ///
     /// @param name name of the observation
     /// @param value integer value observed
     /// @throw InvalidStatType if statistic is not integer
-    void addValue(const std::string& name, uint64_t value);
+    void addValue(const std::string& name, const uint64_t value);
 
-    /// @brief Records incremental floating point observation
+    /// @brief Records incremental floating point observation.
     ///
     /// @param name name of the observation
     /// @param value floating point value observed
     /// @throw InvalidStatType if statistic is not fp
-    void addValue(const std::string& name, double value);
+    void addValue(const std::string& name, const double value);
 
-    /// @brief Records incremental duration observation
+    /// @brief Records incremental duration observation.
     ///
     /// @param name name of the observation
     /// @param value duration value observed
     /// @throw InvalidStatType if statistic is not time duration
-    void addValue(const std::string& name, StatsDuration time);
+    void addValue(const std::string& name, const StatsDuration& time);
 
     /// @brief Records incremental string observation.
     ///
@@ -116,8 +116,7 @@ class StatsMgr : public boost::noncopyable {
     /// @throw InvalidStatType if statistic is not a string
     void addValue(const std::string& name, const std::string& value);
 
-    /// @brief determines whether a given statistic is kept as a single value
-    ///        or as a number of values
+    /// @brief Determines maximum age of samples.
     ///
     /// Specifies that statistic name should be stored not as a single value,
     /// but rather as a set of values. duration determines the timespan.
@@ -131,10 +130,9 @@ class StatsMgr : public boost::noncopyable {
     /// call setMaxSampleAge("incoming-packets", time_duration(0,5,0,0));
     /// to revert statistic to a single value, call:
     /// setMaxSampleAge("incoming-packets" time_duration(0,0,0,0))
-    void setMaxSampleAge(const std::string& name,
-                         boost::posix_time::time_duration duration);
+    void setMaxSampleAge(const std::string& name, const StatsDuration& duration);
 
-    /// @brief determines how many samples of a given statistic should be kept.
+    /// @brief Determines how many samples of a given statistic should be kept.
     ///
     /// Specifies that statistic name should be stored not as single value, but
     /// rather as a set of values. In this form, at most max_samples will be kept.
@@ -164,6 +162,7 @@ class StatsMgr : public boost::noncopyable {
     bool reset(const std::string& name);
 
     /// @brief Removes specified statistic.
+    ///
     /// @param name name of the statistic to be removed.
     /// @return true if successful, false if there's no such statistic
     bool del(const std::string& name);
@@ -175,20 +174,23 @@ class StatsMgr : public boost::noncopyable {
     void removeAll();
 
     /// @brief Returns number of available statistics.
+    ///
     /// @return number of recorded statistics.
     size_t count() const;
 
-    /// @brief Returns a single statistic as a JSON structure
+    /// @brief Returns a single statistic as a JSON structure.
+    ///
     /// @return JSON structures representing a single statistic
     isc::data::ConstElementPtr get(const std::string& name) const;
 
-    /// @brief Returns all statistics as a JSON structure
+    /// @brief Returns all statistics as a JSON structure.
+    ///
     /// @return JSON structures representing all statistics
     isc::data::ConstElementPtr getAll() const;
 
     /// @}
 
-    /// @brief Returns an observation
+    /// @brief Returns an observation.
     ///
     /// Used in testing only. Production code should use @ref get() method.
     /// @param name name of the statistic
@@ -197,13 +199,13 @@ class StatsMgr : public boost::noncopyable {
 
  private:
 
-    /// @brief Sets a given statistic to specified value (internal version)
+    /// @brief Sets a given statistic to specified value (internal version).
     ///
     /// This template method sets statistic identified by name to a value
     /// specified by value. This internal method is used by public @ref setValue
     /// methods.
     ///
-    /// @tparam DataType one of uint64_t, double DurationStat or string
+    /// @tparam DataType one of uint64_t, double, StatsDuration or string
     /// @param name name of the statistic
     /// @param value specified statistic will be set to this value
     /// @throw InvalidStatType is statistic exists and has a different type.
@@ -218,13 +220,13 @@ class StatsMgr : public boost::noncopyable {
         }
     }
 
-    /// @brief Adds specified value to a given statistic (internal version)
+    /// @brief Adds specified value to a given statistic (internal version).
     ///
     /// This template method adds specified value to a given statistic (identified
     /// by name to a value). This internal method is used by public @ref setValue
     /// methods.
     ///
-    /// @tparam DataType one of uint64_t, double DurationStat or string
+    /// @tparam DataType one of uint64_t, double, StatsDuration or string
     /// @param name name of the statistic
     /// @param value specified statistic will be set to this value
     /// @throw InvalidStatType is statistic exists and has a different type.
@@ -244,25 +246,25 @@ class StatsMgr : public boost::noncopyable {
         }
     }
 
-    /// @brief Private constructor
+    /// @brief Private constructor.
     /// StatsMgr is a singleton. It should be accessed using @ref instance
     /// method.
     StatsMgr();
 
-    /// @brief Adds a new observation
+    /// @brief Adds a new observation.
     ///
     /// That's an utility method used by public @ref setValue() and
     /// @ref addValue() methods.
     /// @param obs observation
     void addObservation(const ObservationPtr& o);
 
-    /// @brief Tries to delete an observation
+    /// @brief Tries to delete an observation.
     ///
     /// @param name of the statistic to be deleted
     /// @return true if deleted, false if not found
     bool deleteObservation(const std::string& name);
 
-    // This is a global context. All stats will initially be stored here.
+    // This is a global context. All statistics will initially be stored here.
     StatContextPtr global_;
 };
 

+ 1 - 1
src/lib/stats/tests/Makefile.am

@@ -25,8 +25,8 @@ libstats_unittests_CXXFLAGS = $(AM_CXXFLAGS)
 libstats_unittests_LDADD  = $(top_builddir)/src/lib/stats/libkea-stats.la
 libstats_unittests_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la
 libstats_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
+libstats_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
 libstats_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
-libstats_unittests_LDADD  += -lboost_date_time
 libstats_unittests_LDADD += $(GTEST_LDADD)
 
 endif

+ 23 - 15
src/lib/stats/tests/observation_unittest.cc

@@ -16,7 +16,7 @@
 
 #include <stats/observation.h>
 #include <exceptions/exceptions.h>
-
+#include <util/boost_time_utils.h>
 #include <boost/shared_ptr.hpp>
 #include <boost/date_time/posix_time/posix_time_types.hpp>
 #include <gtest/gtest.h>
@@ -32,8 +32,15 @@ using namespace boost::posix_time;
 
 namespace {
 
+/// @brief Test class for Observation
+///
+/// This simple fixture class initializes four observations:
+/// a (integer), b (float), c(time duration) and d (string).
 class ObservationTest : public ::testing::Test {
 public:
+
+    /// @brief Constructor
+    /// Initializes four observations.
     ObservationTest()
         :a("alpha", static_cast<uint64_t>(1234)), // integer
          b("beta", 12.34), // float
@@ -47,8 +54,8 @@ public:
     Observation d;
 };
 
-// Basic tests for V4 functionality. This test checks whether parameters
-// passed to constructor initialize the object properly.
+// Basic tests for the Obseration constructors. This test checks whether
+// parameters passed to the constructor initialize the object properly.
 TEST_F(ObservationTest, constructor) {
 
     EXPECT_EQ(Observation::STAT_INTEGER, a.getType());
@@ -137,12 +144,12 @@ TEST_F(ObservationTest, addValue) {
 
 // Test checks whether timing is reported properly.
 TEST_F(ObservationTest, timers) {
-    ptime min = microsec_clock::local_time();
-    b.setValue(123.0); // set it to a random value
+    ptime before = microsec_clock::local_time();
+    b.setValue(123.0); // Set it to a random value and record the time.
 
-    // Allow a bit of inprecision. This test allows 5ms. That's ok, when running
-    // on virtual machines.
-    ptime max = min + millisec::time_duration(0,0,0,5);
+    // Allow a bit of inprecision. This test allows 50ms. That should be ok,
+    // when running on virtual machines.
+    ptime after = before + millisec::time_duration(0,0,0,50);
 
     // Now wait some time. We want to confirm that the timestamp recorded is the
     // time the observation took place, not current time.
@@ -150,9 +157,10 @@ TEST_F(ObservationTest, timers) {
 
     FloatSample sample = b.getFloat();
 
-    // Let's check that the timestamp is within (min,max) range.
-    EXPECT_TRUE(min <= sample.second);
-    EXPECT_TRUE(sample.second <= max);
+    // Let's check that the timestamp is within (before,after) range:
+    // before < sample-time < after
+    EXPECT_TRUE(before <= sample.second);
+    EXPECT_TRUE(sample.second <= after);
 }
 
 // Checks whether an integer statistic can generate proper JSON structures.
@@ -162,7 +170,7 @@ TEST_F(ObservationTest, integerToJSON) {
     a.setValue(static_cast<uint64_t>(1234));
 
     std::string exp = "[ [ 1234, \""
-        + Observation::ptimeToText(a.getInteger().second) + "\" ] ]";
+        + isc::util::ptimeToText(a.getInteger().second) + "\" ] ]";
 
     std::cout << a.getJSON()->str() << std::endl;
     EXPECT_EQ(exp, a.getJSON()->str());
@@ -177,7 +185,7 @@ TEST_F(ObservationTest, floatToJSON) {
     b.setValue(1234.5);
 
     std::string exp = "[ [ 1234.5, \""
-        + Observation::ptimeToText(b.getFloat().second) + "\" ] ]";
+        + isc::util::ptimeToText(b.getFloat().second) + "\" ] ]";
 
     std::cout << b.getJSON()->str() << std::endl;
     EXPECT_EQ(exp, b.getJSON()->str());
@@ -191,7 +199,7 @@ TEST_F(ObservationTest, durationToJSON) {
     c.setValue(time_duration(1,2,3,4));
 
     std::string exp = "[ [ \"01:02:03.000004\", \""
-        + Observation::ptimeToText(c.getDuration().second) + "\" ] ]";
+        + isc::util::ptimeToText(c.getDuration().second) + "\" ] ]";
 
     std::cout << c.getJSON()->str() << std::endl;
     EXPECT_EQ(exp, c.getJSON()->str());
@@ -205,7 +213,7 @@ TEST_F(ObservationTest, stringToJSON) {
     d.setValue("Lorem ipsum dolor sit amet");
 
     std::string exp = "[ [ \"Lorem ipsum dolor sit amet\", \""
-        + Observation::ptimeToText(d.getString().second) + "\" ] ]";
+        + isc::util::ptimeToText(d.getString().second) + "\" ] ]";
 
     std::cout << d.getJSON()->str() << std::endl;
     EXPECT_EQ(exp, d.getJSON()->str());

+ 26 - 19
src/lib/stats/tests/stats_mgr_unittest.cc

@@ -16,9 +16,10 @@
 
 #include <stats/stats_mgr.h>
 #include <exceptions/exceptions.h>
+#include <cc/data.h>
+#include <util/boost_time_utils.h>
 #include <boost/date_time/posix_time/posix_time_types.hpp>
 #include <boost/shared_ptr.hpp>
-#include <cc/data.h>
 #include <gtest/gtest.h>
 
 #include <iostream>
@@ -31,12 +32,20 @@ using namespace boost::posix_time;
 
 namespace {
 
+/// @brief Fixture class for StatsMgr testing
+///
+/// Very simple class that makes sure that StatsMgr is indeed instantiated
+/// before the test and any statistics are wiped out after it.
 class StatsMgrTest : public ::testing::Test {
 public:
+    /// @brief Constructor
+    /// Makes sure that the Statistics Manager is instantiated.
     StatsMgrTest() {
         StatsMgr::instance();
     }
 
+    /// @brief Destructor
+    /// Removes all statistics.
     ~StatsMgrTest() {
         StatsMgr::instance().removeAll();
     }
@@ -60,10 +69,10 @@ TEST_F(StatsMgrTest, integerStat) {
 
     ObservationPtr alpha;
     EXPECT_NO_THROW(alpha = StatsMgr::instance().getObservation("alpha"));
-    EXPECT_TRUE(alpha);
+    ASSERT_TRUE(alpha);
 
     std::string exp = "{ \"alpha\": [ [ 1234, \""
-        + Observation::ptimeToText(alpha->getInteger().second) + "\" ] ] }";
+        + isc::util::ptimeToText(alpha->getInteger().second) + "\" ] ] }";
 
     EXPECT_EQ(exp, StatsMgr::instance().get("alpha")->str());
 }
@@ -75,10 +84,10 @@ TEST_F(StatsMgrTest, floatStat) {
 
     ObservationPtr beta;
     EXPECT_NO_THROW(beta = StatsMgr::instance().getObservation("beta"));
-    EXPECT_TRUE(beta);
+    ASSERT_TRUE(beta);
 
     std::string exp = "{ \"beta\": [ [ 12.34, \""
-        + Observation::ptimeToText(beta->getFloat().second) + "\" ] ] }";
+        + isc::util::ptimeToText(beta->getFloat().second) + "\" ] ] }";
 
     EXPECT_EQ(exp, StatsMgr::instance().get("beta")->str());
 }
@@ -91,10 +100,10 @@ TEST_F(StatsMgrTest, durationStat) {
 
     ObservationPtr gamma;
     EXPECT_NO_THROW(gamma = StatsMgr::instance().getObservation("gamma"));
-    EXPECT_TRUE(gamma);
+    ASSERT_TRUE(gamma);
 
     std::string exp = "{ \"gamma\": [ [ \"01:02:03.000004\", \""
-        + Observation::ptimeToText(gamma->getDuration().second) + "\" ] ] }";
+        + isc::util::ptimeToText(gamma->getDuration().second) + "\" ] ] }";
 
     EXPECT_EQ(exp, StatsMgr::instance().get("gamma")->str());
 }
@@ -107,10 +116,10 @@ TEST_F(StatsMgrTest, stringStat) {
 
     ObservationPtr delta;
     EXPECT_NO_THROW(delta = StatsMgr::instance().getObservation("delta"));
-    EXPECT_TRUE(delta);
+    ASSERT_TRUE(delta);
 
     std::string exp = "{ \"delta\": [ [ \"Lorem ipsum\", \""
-        + Observation::ptimeToText(delta->getString().second) + "\" ] ] }";
+        + isc::util::ptimeToText(delta->getString().second) + "\" ] ] }";
 
     EXPECT_EQ(exp, StatsMgr::instance().get("delta")->str());
 }
@@ -157,16 +166,16 @@ TEST_F(StatsMgrTest, getGetAll) {
     ASSERT_TRUE(rep_delta);
 
     std::string exp_str_alpha = "[ [ 6912, \""
-        + Observation::ptimeToText(StatsMgr::instance().getObservation("alpha")
+        + isc::util::ptimeToText(StatsMgr::instance().getObservation("alpha")
                                    ->getInteger().second) + "\" ] ]";
     std::string exp_str_beta = "[ [ 69.12, \""
-        + Observation::ptimeToText(StatsMgr::instance().getObservation("beta")
+        + isc::util::ptimeToText(StatsMgr::instance().getObservation("beta")
                                    ->getFloat().second) + "\" ] ]";
     std::string exp_str_gamma = "[ [ \"06:08:10.000012\", \""
-        + Observation::ptimeToText(StatsMgr::instance().getObservation("gamma")
+        + isc::util::ptimeToText(StatsMgr::instance().getObservation("gamma")
                                    ->getDuration().second) + "\" ] ]";
     std::string exp_str_delta = "[ [ \"Lorem ipsum\", \""
-        + Observation::ptimeToText(StatsMgr::instance().getObservation("delta")
+        + isc::util::ptimeToText(StatsMgr::instance().getObservation("delta")
                                    ->getString().second) + "\" ] ]";
 
     // Check that individual stats are reported properly
@@ -182,8 +191,6 @@ TEST_F(StatsMgrTest, getGetAll) {
     ConstElementPtr rep_all = StatsMgr::instance().getAll();
     ASSERT_TRUE(rep_all);
 
-    std::cout << rep_all->str() << std::endl;
-
     // Verifying this is a bit more involved, as we don't know whether the
     // order would be preserved or not.
     EXPECT_EQ(4, rep_all->size());
@@ -305,7 +312,7 @@ TEST_F(StatsMgrTest, DISABLED_performanceSingleAdd) {
     time_duration dur = after - before;
 
     std::cout << "Incrementing a single statistic " << cycles << " times took: "
-              << Observation::durationToText(dur) << std::endl;
+              << isc::util::durationToText(dur) << std::endl;
 }
 
 // This is a performance benchmark that checks how long does it take
@@ -327,7 +334,7 @@ TEST_F(StatsMgrTest, DISABLED_performanceSingleSet) {
     time_duration dur = after - before;
 
     std::cout << "Setting a single statistic " << cycles << " times took: "
-              << Observation::durationToText(dur) << std::endl;
+              << isc::util::durationToText(dur) << std::endl;
 }
 
 // This is a performance benchmark that checks how long does it take to
@@ -357,7 +364,7 @@ TEST_F(StatsMgrTest, DISABLED_performanceMultipleAdd) {
     time_duration dur = after - before;
 
     std::cout << "Incrementing one of " << stats << " statistics " << cycles
-              << " times took: " << Observation::durationToText(dur) << std::endl;
+              << " times took: " << isc::util::durationToText(dur) << std::endl;
 }
 
 // This is a performance benchmark that checks how long does it take to
@@ -387,7 +394,7 @@ TEST_F(StatsMgrTest, DISABLED_performanceMultipleSet) {
     time_duration dur = after - before;
 
     std::cout << "Setting one of " << stats << " statistics " << cycles
-              << " times took: " << Observation::durationToText(dur) << std::endl;
+              << " times took: " << isc::util::durationToText(dur) << std::endl;
 }
 
 };

+ 2 - 1
src/lib/util/Makefile.am

@@ -9,7 +9,8 @@ AM_CPPFLAGS += $(BOOST_INCLUDES)
 AM_CXXFLAGS = $(KEA_CXXFLAGS)
 
 lib_LTLIBRARIES = libkea-util.la
-libkea_util_la_SOURCES  = csv_file.h csv_file.cc
+libkea_util_la_SOURCES  = boost_time_utils.h boost_time_utils.cc
+libkea_util_la_SOURCES += csv_file.h csv_file.cc
 libkea_util_la_SOURCES += filename.h filename.cc
 libkea_util_la_SOURCES += locks.h lru_list.h
 libkea_util_la_SOURCES += strutil.h strutil.cc

+ 37 - 0
src/lib/util/boost_time_utils.cc

@@ -0,0 +1,37 @@
+// 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 <util/boost_time_utils.h>
+
+std::string
+isc::util::ptimeToText(boost::posix_time::ptime t) {
+    boost::gregorian::date d = t.date();
+    std::stringstream s;
+    s << d.year() << "-" << d.month() << "-" << d.day();
+    s << " " << durationToText(t.time_of_day());
+    return (s.str());
+}
+
+std::string
+isc::util::durationToText(boost::posix_time::time_duration dur) {
+    std::stringstream s;
+    s << std::setw(2) << std::setfill('0') << dur.hours()
+      << ":" << std::setw(2) << std::setfill('0') << dur.minutes()
+      << ":" << std::setw(2) << std::setfill('0') << dur.seconds()
+      << "." << std::setw(boost::posix_time::time_duration::num_fractional_digits())
+      << std::setfill('0')
+      << dur.fractional_seconds();
+
+    return (s.str());
+}

+ 34 - 0
src/lib/util/boost_time_utils.h

@@ -0,0 +1,34 @@
+// 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 KEA_BOOST_TIME_UTILS_H
+#define KEA_BOOST_TIME_UTILS_H
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+namespace isc {
+namespace util {
+
+/// @brief Converts ptime structure to text
+/// @return a string representing time
+std::string ptimeToText(boost::posix_time::ptime t);
+
+/// @brief Converts StatsDuration to text
+/// @return a string representing time
+std::string durationToText(boost::posix_time::time_duration dur);
+
+}; // end of isc::util namespace
+}; // end of isc namespace
+
+#endif