]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
Support for storing more than one sample
authorFranciszek Gorski <fagorski9@gmail.com>
Thu, 11 Jul 2019 11:25:24 +0000 (13:25 +0200)
committerFranciszek Gorski <fagorski9@gmail.com>
Mon, 15 Jul 2019 10:34:52 +0000 (12:34 +0200)
src/bin/dhcp4/tests/shared_network_unittest.cc
src/bin/dhcp6/tests/shared_network_unittest.cc
src/lib/stats/observation.cc
src/lib/stats/observation.h
src/lib/stats/stats_mgr.cc
src/lib/stats/stats_mgr.h
src/lib/stats/tests/observation_unittest.cc
src/lib/stats/tests/stats_mgr_unittest.cc

index 247b13ceadceddedd3852425a9d087ffbfabb6d3..2a6720b42cd5831952f12daa8277cf8acc5d73f2 100644 (file)
@@ -1192,8 +1192,7 @@ public:
                 // Get the nested list which should have two elements, of which first
                 // is the statistics value we're looking for.
                 ConstElementPtr second_list = first_list->get(0);
-                if (second_list && (second_list->getType() == Element::list) &&
-                    (second_list->size() == 2)) {
+                if (second_list && (second_list->getType() == Element::list)) {
                     ConstElementPtr addresses_element = second_list->get(0);
                     if (addresses_element && (addresses_element->getType() == Element::integer)) {
                         return (addresses_element->intValue());
index c31ec68b8aa97ac063f4abd845f666bbe3f94cb7..87c403d1364a51514c3caff4b67e22775e49efe2 100644 (file)
@@ -1171,8 +1171,7 @@ public:
                 // Get the nested list which should have two elements, of which first
                 // is the statistics value we're looking for.
                 ConstElementPtr second_list = first_list->get(0);
-                if (second_list && (second_list->getType() == Element::list) &&
-                    (second_list->size() == 2)) {
+                if (second_list && (second_list->getType() == Element::list)) {
                     ConstElementPtr addresses_element = second_list->get(0);
                     if (addresses_element && (addresses_element->getType() == Element::integer)) {
                         return (addresses_element->intValue());
index 61155bae126f0b69b8997c6f2cb7d65e70265bf5..950ed74c6c2678c318912d6a15e3a2ffc76cbe69 100644 (file)
@@ -20,26 +20,74 @@ using namespace boost::posix_time;
 namespace isc {
 namespace stats {
 
-Observation::Observation(const std::string& name, const int64_t value)
-    :name_(name), type_(STAT_INTEGER) {
+Observation::Observation(const std::string& name, const int64_t value) :
+    name_(name), type_(STAT_INTEGER) {
     setValue(value);
 }
 
-Observation::Observation(const std::string& name, const double value)
-    :name_(name), type_(STAT_FLOAT) {
+Observation::Observation(const std::string& name, const double value) :
+    name_(name), type_(STAT_FLOAT) {
     setValue(value);
 }
 
-Observation::Observation(const std::string& name, const StatsDuration& value)
-    :name_(name), type_(STAT_DURATION) {
+Observation::Observation(const std::string& name, const StatsDuration& value) :
+    name_(name), type_(STAT_DURATION) {
     setValue(value);
 }
 
-Observation::Observation(const std::string& name, const std::string& value)
-    :name_(name), type_(STAT_STRING) {
+Observation::Observation(const std::string& name, const std::string& value) :
+    name_(name), type_(STAT_STRING) {
     setValue(value);
 }
 
+void Observation::setMaxSampleAge(const StatsDuration& duration) {
+    switch(type_) {
+    case STAT_INTEGER: {
+        setMaxSampleAgeInternal(integer_samples_, duration, STAT_INTEGER);
+        return;
+    }
+    case STAT_FLOAT: {
+        setMaxSampleAgeInternal(float_samples_, duration, STAT_FLOAT);
+        return;
+    }
+    case STAT_DURATION: {
+        setMaxSampleAgeInternal(duration_samples_, duration, STAT_DURATION);
+        return;
+    }
+    case STAT_STRING: {
+        setMaxSampleAgeInternal(string_samples_, duration, STAT_STRING);
+        return;
+    }
+    default:
+        isc_throw(InvalidStatType, "Unknown statistic type: "
+                  << typeToText(type_));
+    };
+}
+
+void Observation::setMaxSampleCount(uint32_t max_samples) {
+    switch(type_) {
+    case STAT_INTEGER: {
+        setMaxSampleCountInternal(integer_samples_, max_samples, STAT_INTEGER);
+        return;
+    }
+    case STAT_FLOAT: {
+        setMaxSampleCountInternal(float_samples_, max_samples, STAT_FLOAT);
+        return;
+    }
+    case STAT_DURATION: {
+        setMaxSampleCountInternal(duration_samples_, max_samples, STAT_DURATION);
+        return;
+    }
+    case STAT_STRING: {
+        setMaxSampleCountInternal(string_samples_, max_samples, STAT_STRING);
+        return;
+    }
+    default:
+        isc_throw(InvalidStatType, "Unknown statistic type: "
+                  << typeToText(type_));
+    };
+}
+
 void Observation::addValue(const int64_t value) {
     IntegerSample current = getInteger();
     setValue(current.first + value);
@@ -76,21 +124,76 @@ void Observation::setValue(const std::string& value) {
     setValueInternal(value, string_samples_, STAT_STRING);
 }
 
+size_t Observation::getSize() const {
+    size_t size = 0;
+    switch(type_) {
+    case STAT_INTEGER: {
+        size = getSizeInternal(integer_samples_, STAT_INTEGER);
+        return (size);
+    }
+    case STAT_FLOAT: {
+        size = getSizeInternal(float_samples_, STAT_FLOAT);
+        return (size);
+    }
+    case STAT_DURATION: {
+        size = getSizeInternal(duration_samples_, STAT_DURATION);
+        return (size);
+    }
+    case STAT_STRING: {
+        size = getSizeInternal(string_samples_, STAT_STRING);
+        return (size);
+    }
+    default:
+        isc_throw(InvalidStatType, "Unknown statistic type: "
+                  << typeToText(type_));
+    };
+    return (size);
+}
+
+template<typename StorageType>
+size_t Observation::getSizeInternal(StorageType& storage, Type exp_type) const {
+    if (type_ != exp_type) {
+        isc_throw(InvalidStatType, "Invalid statistic type requested: "
+                  << typeToText(exp_type) << ", but the actual type is "
+                  << typeToText(type_));
+    } else {
+        return (storage.size());
+    }
+    return (0); // to avoid compilation error
+}
+
 template<typename SampleType, typename StorageType>
 void Observation::setValueInternal(SampleType value, StorageType& storage,
     Type exp_type) {
     if (type_ != exp_type) {
         isc_throw(InvalidStatType, "Invalid statistic type requested: "
                   << typeToText(exp_type) << ", but the actual type is "
-                  << typeToText(type_) );
+                  << typeToText(type_));
     }
 
     if (storage.empty()) {
         storage.push_back(make_pair(value, microsec_clock::local_time()));
     } else {
-
-        /// @todo: Update once more than one sample is supported
-        *storage.begin() = make_pair(value, microsec_clock::local_time());
+        // Storing of more than one sample
+        storage.push_front(make_pair(value, microsec_clock::local_time()));
+
+        if (max_sample_count_.first) {
+            // if max_sample_count is set to true
+            // and size of storage is equal to max_sample_count
+            if (storage.size() > max_sample_count_.second) {
+                storage.pop_back(); // removing the last element
+            }
+        } else {
+            StatsDuration range_of_storage =
+                storage.front().second - storage.back().second;
+            // removing samples until the range_of_storage
+            // stops exceeding the duration limit
+            while (range_of_storage > max_sample_age_.second) {
+                storage.pop_back();
+                range_of_storage =
+                    storage.front().second - storage.back().second;
+            }
+        }
     }
 }
 
@@ -115,7 +218,7 @@ SampleType Observation::getValueInternal(Storage& storage, Type exp_type) const
     if (type_ != exp_type) {
         isc_throw(InvalidStatType, "Invalid statistic type requested: "
                   << typeToText(exp_type) << ", but the actual type is "
-                  << typeToText(type_) );
+                  << typeToText(type_));
     }
 
     if (storage.empty()) {
@@ -127,6 +230,83 @@ SampleType Observation::getValueInternal(Storage& storage, Type exp_type) const
     return (*storage.begin());
 }
 
+std::list<IntegerSample> Observation::getIntegers() const {
+    return (getValuesInternal<IntegerSample>(integer_samples_, STAT_INTEGER));
+}
+
+std::list<FloatSample> Observation::getFloats() const {
+    return (getValuesInternal<FloatSample>(float_samples_, STAT_FLOAT));
+}
+
+std::list<DurationSample> Observation::getDurations() const {
+    return (getValuesInternal<DurationSample>(duration_samples_, STAT_DURATION));
+}
+
+std::list<StringSample> Observation::getStrings() const {
+    return (getValuesInternal<StringSample>(string_samples_, STAT_STRING));
+}
+
+template<typename SampleType, typename Storage>
+std::list<SampleType> Observation::getValuesInternal(Storage& storage, Type exp_type) const {
+    if (type_ != exp_type) {
+        isc_throw(InvalidStatType, "Invalid statistic type requested: "
+                  << typeToText(exp_type) << ", but the actual type is "
+                  << typeToText(type_));
+    }
+
+    if (storage.empty()) {
+        // That should never happen. The first element is always initialized in
+        // the constructor. reset() sets its value to zero, but the element should
+        // still be there.
+        isc_throw(Unexpected, "Observation storage container empty");
+    }
+    return (storage);
+}
+
+template<typename StorageType>
+void Observation::setMaxSampleAgeInternal(StorageType& storage,
+    const StatsDuration& duration, Type exp_type) {
+    if (type_ != exp_type) {
+        isc_throw(InvalidStatType, "Invalid statistic type requested: "
+                  << typeToText(exp_type) << ", but the actual type is "
+                  << typeToText(type_));
+    }
+    // setting new value of max_sample_age
+    max_sample_age_.first = true;
+    max_sample_age_.second = duration;
+    // deactivating the max_sample_count limit
+    max_sample_count_.first = false;
+
+    StatsDuration range_of_storage =
+        storage.front().second - storage.back().second;
+
+    while (range_of_storage > duration) {
+        // deleting elements which are exceeding the duration limit
+        storage.pop_back();
+        range_of_storage = storage.front().second - storage.back().second;
+    }
+}
+
+template<typename StorageType>
+void Observation::setMaxSampleCountInternal(StorageType& storage,
+    uint32_t max_samples, Type exp_type) {
+    if (type_ != exp_type) {
+        isc_throw(InvalidStatType, "Invalid statistic type requested: "
+                  << typeToText(exp_type) << ", but the actual type is "
+                  << typeToText(type_));
+    }
+    // setting new value of max_sample_count
+    max_sample_count_.first = true;
+    max_sample_count_.second = max_samples;
+    // deactivating the max_sample_age limit
+    max_sample_age_.first = false;
+
+    while (storage.size() > max_samples) {
+        // deleting elements which are exceeding the max_samples limit
+        storage.pop_back();
+    }
+}
+
 std::string Observation::typeToText(Type type) {
     std::stringstream tmp;
     switch (type) {
@@ -153,36 +333,67 @@ std::string Observation::typeToText(Type type) {
 isc::data::ConstElementPtr
 Observation::getJSON() const {
 
-    ElementPtr entry = isc::data::Element::createList(); // a single observation
+    ElementPtr entry = isc::data::Element::createList(); // multiple observations
     ElementPtr value;
     ElementPtr timestamp;
 
-    /// @todo: Add support for retrieving more than one sample for a given
-    /// observation
-
+    // Support for retrieving more than one sample
+    // retrieving all samples of indicated observation
     switch (type_) {
     case STAT_INTEGER: {
-        IntegerSample s = getInteger();
-        value = isc::data::Element::create(static_cast<int64_t>(s.first));
-        timestamp = isc::data::Element::create(isc::util::ptimeToText(s.second));
+        std::list<IntegerSample> s = getIntegers(); // List of all integer samples
+
+        // Iteration over all elements in the list
+        // and adding alternately value and timestamp to the entry
+        for (std::list<IntegerSample>::iterator it = s.begin(); it != s.end(); ++it) {
+            value = isc::data::Element::create(static_cast<int64_t>((*it).first));
+            timestamp = isc::data::Element::create(isc::util::ptimeToText((*it).second));
+
+            entry->add(value);
+            entry->add(timestamp);
+        }
         break;
     }
     case STAT_FLOAT: {
-        FloatSample s = getFloat();
-        value = isc::data::Element::create(s.first);
-        timestamp = isc::data::Element::create(isc::util::ptimeToText(s.second));
+        std::list<FloatSample> s = getFloats();
+
+        // Iteration over all elements in the list
+        // and adding alternately value and timestamp to the entry
+        for (std::list<FloatSample>::iterator it = s.begin(); it != s.end(); ++it) {
+            value = isc::data::Element::create((*it).first);
+            timestamp = isc::data::Element::create(isc::util::ptimeToText((*it).second));
+
+            entry->add(value);
+            entry->add(timestamp);
+        }
         break;
     }
     case STAT_DURATION: {
-        DurationSample s = getDuration();
-        value = isc::data::Element::create(isc::util::durationToText(s.first));
-        timestamp = isc::data::Element::create(isc::util::ptimeToText(s.second));
+        std::list<DurationSample> s = getDurations();
+
+        // Iteration over all elements in the list
+        // and adding alternately value and timestamp to the entry
+        for (std::list<DurationSample>::iterator it = s.begin(); it != s.end(); ++it) {
+            value = isc::data::Element::create(isc::util::durationToText((*it).first));
+            timestamp = isc::data::Element::create(isc::util::ptimeToText((*it).second));
+
+            entry->add(value);
+            entry->add(timestamp);
+        }
         break;
     }
     case STAT_STRING: {
-        StringSample s = getString();
-        value = isc::data::Element::create(s.first);
-        timestamp = isc::data::Element::create(isc::util::ptimeToText(s.second));
+        std::list<StringSample> s = getStrings();
+
+        // Iteration over all elements in the list
+        // and adding alternately value and timestamp to the entry
+        for (std::list<StringSample>::iterator it = s.begin(); it != s.end(); ++it) {
+            value = isc::data::Element::create((*it).first);
+            timestamp = isc::data::Element::create(isc::util::ptimeToText((*it).second));
+
+            entry->add(value);
+            entry->add(timestamp);
+        }
         break;
     }
     default:
@@ -190,10 +401,7 @@ Observation::getJSON() const {
                   << typeToText(type_));
     };
 
-    entry->add(value);
-    entry->add(timestamp);
-
-    ElementPtr list = isc::data::Element::createList(); // a single observation
+    ElementPtr list = isc::data::Element::createList(); // multiple observations
     list->add(entry);
 
     return (list);
@@ -202,18 +410,22 @@ Observation::getJSON() const {
 void Observation::reset() {
     switch(type_) {
     case STAT_INTEGER: {
+        integer_samples_.clear();
         setValue(static_cast<int64_t>(0));
         return;
     }
     case STAT_FLOAT: {
+        float_samples_.clear();
         setValue(0.0);
         return;
     }
     case STAT_DURATION: {
-        setValue(time_duration(0,0,0,0));
+        duration_samples_.clear();
+        setValue(time_duration(0, 0, 0, 0));
         return;
     }
     case STAT_STRING: {
+        string_samples_.clear();
         setValue(string(""));
         return;
     }
index 586d3a0fc6e6a3024afba5082e260d71d42344ae..086898fd54cb75671de5c92c79211cb1dd6f197e 100644 (file)
@@ -80,7 +80,7 @@ class Observation {
     /// int64_t and double. If convincing use cases appear to change them
     /// to something else, we may change the underlying type.
     enum Type {
-        STAT_INTEGER, ///< this statistic is unsinged 64-bit integer value
+        STAT_INTEGER, ///< this statistic is unsigned 64-bit integer value
         STAT_FLOAT,   ///< this statistic is a floating point value
         STAT_DURATION,///< this statistic represents time duration
         STAT_STRING   ///< this statistic represents a string
@@ -110,6 +110,36 @@ class Observation {
     /// @param value string observed.
     Observation(const std::string& name, const std::string& value);
 
+    /// @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. The duration determines the timespan.
+    /// Samples older than duration will be discarded. This is time-constrained
+    /// approach. For sample count constrained approach, see @ref
+    /// setMaxSampleCount() below.
+    ///
+    ///
+    /// @param duration determines maximum age of samples
+    /// Example:
+    /// To set a statistic to keep observations for the last 5 minutes,
+    /// call: setMaxSampleAge(time_duration(0, 5, 0, 0));
+    /// To revert statistic to a single value, call:
+    /// setMaxSampleAge(time_duration(0, 0, 0, 0))
+    void setMaxSampleAge(const StatsDuration& duration);
+
+    /// @brief Determines how many samples of a given statistic should be kept.
+    ///
+    /// Specifies that statistic name should be stored not as a single value,
+    /// but rather as a set of values. In this form, at most max_samples will
+    /// be kept. When adding max_samples + 1 sample, the oldest sample will be
+    /// discarded.
+    ///
+    /// @param max_samples how many samples of a given statistic should be kept
+    /// Example:
+    /// To set a statistic to keep the last 100 observations, call:
+    /// setMaxSampleCount(100);
+    void setMaxSampleCount(uint32_t max_samples);
+
     /// @brief Records absolute integer observation
     ///
     /// @param value integer value observed
@@ -158,9 +188,15 @@ class Observation {
     /// @throw InvalidStatType if statistic is not a string
     void addValue(const std::string& value);
 
+    /// @brief Returns size of observed storage
+    ///
+    /// @return size of storage
+    size_t getSize() const;
+
     /// @brief Resets statistic.
     ///
-    /// Sets statistic to a neutral (0, 0.0 or "") value.
+    /// Sets statistic to a neutral (0, 0.0 or "") value and
+    /// clears the underlying storage.
     void reset();
 
     /// @brief Returns statistic type
@@ -189,6 +225,26 @@ class Observation {
     /// @throw InvalidStatType if statistic is not a string
     StringSample getString() const;
 
+    /// @brief Returns observed integer samples
+    /// @return list of observed samples (value + timestamp)
+    /// @throw InvalidStatType if statistic is not integer
+    std::list<IntegerSample> getIntegers() const;
+
+    /// @brief Returns observed float samples
+    /// @return list of observed samples (value + timestamp)
+    /// @throw InvalidStatType if statistic is not fp
+    std::list<FloatSample> getFloats() const;
+
+    /// @brief Returns observed duration samples
+    /// @return list of observed samples (value + timestamp)
+    /// @throw InvalidStatType if statistic is not time duration
+    std::list<DurationSample> getDurations() const;
+
+    /// @brief Returns observed string samples
+    /// @return list of observed samples (value + timestamp)
+    /// @throw InvalidStatType if statistic is not a string
+    std::list<StringSample> getStrings() const;
+
     /// @brief Returns as a JSON structure
     /// @return JSON structures representing all observations
     isc::data::ConstElementPtr getJSON() const;
@@ -203,6 +259,19 @@ class Observation {
     }
 
 private:
+
+    /// @brief Returns size of observed storage
+    ///
+    /// This method returns size of observed storage.
+    /// It is used by public methods to return size of
+    /// available storages.
+    /// @tparam Storage type of storage (e.g. list<IntegerSample>)
+    /// @param storage storage which size will be returned
+    /// @param exp_type expected observation type (used for sanity checking)
+    /// @return Size of storage
+    template<typename StorageType>
+    size_t getSizeInternal(StorageType& storage, Type exp_type) const;
+
     /// @brief Records absolute sample (internal version)
     ///
     /// This method records an absolute value of an observation.
@@ -230,12 +299,67 @@ private:
     template<typename SampleType, typename Storage>
     SampleType getValueInternal(Storage& storage, Type exp_type) const;
 
+    /// @brief Returns samples (internal version)
+    ///
+    /// @tparam SampleType type of samples (e.g. IntegerSample)
+    /// @tparam Storage type of storage (e.g. list<IntegerSample>)
+    /// @param observation storage
+    /// @param exp_type expected observation type (used for sanity checking)
+    /// @throw InvalidStatType if observation type mismatches
+    /// @return List of observed samples
+    template<typename SampleType, typename Storage>
+    std::list<SampleType> getValuesInternal(Storage& storage,
+                                            Type exp_type) const;
+
+    /// @brief Determines maximum age of samples.
+    ///
+    /// @tparam Storage type of storage (e.g. list<IntegerSample>)
+    /// @param storage storage on which limit will be set
+    /// @param duration determines maximum age of samples
+    /// @param exp_type expected observation type (used for sanity checking)
+    template<typename StorageType>
+    void setMaxSampleAgeInternal(StorageType& storage,
+                                 const StatsDuration& duration, Type exp_type);
+
+    /// @brief Determines how many samples of a given statistic should be kept.
+    ///
+    /// @tparam Storage type of storage (e.g. list<IntegerSample>)
+    /// @param storage storage on which limit will be set
+    /// @param max_samples determines maximum number of samples
+    /// @param exp_type expected observation type (used for sanity checking)
+    template<typename StorageType>
+    void setMaxSampleCountInternal(StorageType& storage,
+                                   uint32_t max_samples, Type exp_type);
+
     /// @brief Observation (statistic) name
     std::string name_;
 
     /// @brief Observation (statistic) type)
     Type type_;
 
+    /// @brief Maximum number of samples
+    /// The limit is represent as a pair
+    /// of bool value and uint32_t
+    /// Only one kind of limit can be active
+    /// The bool value informs which limit
+    /// is available
+    /// True means active limit, false means inactive limit
+    /// By default the MaxSampleCount is set to 20
+    /// and MaxSampleAge is disabled
+    std::pair<bool, uint32_t> max_sample_count_ = std::make_pair(true, 20);
+
+    /// @brief Maximum timespan of samples
+    /// The limit is represent as a pair
+    /// of bool value and StatsDuration(boost::posix_time::time_duration)
+    /// Only one kind of limit can be active
+    /// The bool value informs which limit
+    /// is available
+    /// True means active limit, false means inactive limit
+    /// By default the MaxSampleCount is set to 20
+    /// and MaxSampleAge is disabled
+    std::pair<bool, StatsDuration> max_sample_age_ = std::make_pair(false,
+            boost::posix_time::time_duration(0, 0, 0, 0));
+
     /// @defgroup samples_storage Storage for supported observations
     ///
     /// @brief The following containers serve as a storage for all supported
index feeee1287b2edb6af831efd5d7d22f74634668d0..910f4a2f8f8064c145469dbd5ff3be7131efb70d 100644 (file)
@@ -10,6 +10,8 @@
 #include <stats/stats_mgr.h>
 #include <cc/data.h>
 #include <cc/command_interpreter.h>
+#include <util/boost_time_utils.h>
+#include <boost/date_time/posix_time/posix_time.hpp>
 
 using namespace std;
 using namespace isc::data;
@@ -23,8 +25,8 @@ StatsMgr& StatsMgr::instance() {
     return (stats_mgr);
 }
 
-StatsMgr::StatsMgr()
-    :global_(new StatContext()) {
+StatsMgr::StatsMgr() :
+    global_(new StatContext()) {
 
 }
 
@@ -77,13 +79,25 @@ bool StatsMgr::deleteObservation(const std::string& name) {
     return (global_->del(name));
 }
 
-void StatsMgr::setMaxSampleAge(const std::string& ,
-                               const StatsDuration&) {
-    isc_throw(NotImplemented, "setMaxSampleAge not implemented");
+bool StatsMgr::setMaxSampleAge(const std::string& name,
+                               const StatsDuration& duration) {
+    ObservationPtr obs = getObservation(name);
+    if (obs) {
+        obs->setMaxSampleAge(duration);
+        return (true);
+    } else {
+        return (false);
+    }
 }
 
-void StatsMgr::setMaxSampleCount(const std::string& , uint32_t){
-    isc_throw(NotImplemented, "setMaxSampleCount not implemented");
+bool StatsMgr::setMaxSampleCount(const std::string& name, uint32_t max_samples) {
+    ObservationPtr obs = getObservation(name);
+    if (obs) {
+        obs->setMaxSampleCount(max_samples);
+        return (true);
+    } else {
+        return (false);
+    }
 }
 
 bool StatsMgr::reset(const std::string& name) {
@@ -108,7 +122,7 @@ isc::data::ConstElementPtr StatsMgr::get(const std::string& name) const {
     isc::data::ElementPtr response = isc::data::Element::createMap(); // a map
     ObservationPtr obs = getObservation(name);
     if (obs) {
-        response->set(name, obs->getJSON()); // that contains the observation
+        response->set(name, obs->getJSON()); // that contains observations
     }
     return (response);
 }
@@ -136,6 +150,15 @@ void StatsMgr::resetAll() {
     }
 }
 
+size_t StatsMgr::getSize(const std::string& name) const {
+    ObservationPtr obs = getObservation(name);
+    size_t size = 0;
+    if (obs) {
+        size = obs->getSize();
+    }
+    return (size);
+}
+
 size_t StatsMgr::count() const {
     return (global_->stats_.size());
 }
index 554e309e9185b8ba7afb04d2cda5a92364c87ff8..86f8d2abb9a0f1d91da34155d7e7b752fc3ef7bf 100644 (file)
@@ -134,13 +134,15 @@ class StatsMgr : public boost::noncopyable {
     /// approach. For sample count constrained approach, see @ref
     /// setMaxSampleCount() below.
     ///
-    /// @todo: Not implemented.
     ///
+    /// @param name name of the observation
+    /// @param duration determines maximum age of samples
+    /// @return true if successful, false if there's no such statistic
     /// Example: to set a statistic to keep observations for the last 5 minutes,
-    /// call setMaxSampleAge("incoming-packets", time_duration(0,5,0,0));
+    /// 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, const StatsDuration& duration);
+    /// setMaxSampleAge("incoming-packets" time_duration(0, 0, 0, 0))
+    bool setMaxSampleAge(const std::string& name, const StatsDuration& duration);
 
     /// @brief Determines how many samples of a given statistic should be kept.
     ///
@@ -148,12 +150,14 @@ class StatsMgr : public boost::noncopyable {
     /// rather as a set of values. In this form, at most max_samples will be kept.
     /// When adding max_samples+1 sample, the oldest sample will be discarded.
     ///
-    /// @todo: Not implemented.
     ///
+    /// @param name name of the observation
+    /// @param max_samples how many samples of a given statistic should be kept
+    /// @return true if successful, false if there's no such statistic
     /// Example:
     /// To set a statistic to keep the last 100 observations, call:
     /// setMaxSampleCount("incoming-packets", 100);
-    void setMaxSampleCount(const std::string& name, uint32_t max_samples);
+    bool setMaxSampleCount(const std::string& name, uint32_t max_samples);
 
     /// @}
 
@@ -183,6 +187,12 @@ class StatsMgr : public boost::noncopyable {
     /// @brief Removes all collected statistics.
     void removeAll();
 
+    /// @brief Returns size of specified statistic.
+    ///
+    /// @param name name of the statistic which size should be return.
+    /// @return size of specified statistic.
+    size_t getSize(const std::string& name) const;
+
     /// @brief Returns number of available statistics.
     ///
     /// @return number of recorded statistics.
@@ -223,7 +233,7 @@ class StatsMgr : public boost::noncopyable {
     /// @return returns full statistic name in form context[index].stat_name
     template<typename Type>
     static std::string generateName(const std::string& context, Type index,
-                             const std::string& stat_name) {
+                                    const std::string& stat_name) {
         std::stringstream name;
         name << context << "[" << index << "]." << stat_name;
         return (name.str());
index 23486cf4ae0d2f62e3237574c07982e2f14b90cb..d9e33b2719877bdd55aa92797dd9b1b206969a06 100644 (file)
@@ -33,11 +33,11 @@ public:
 
     /// @brief Constructor
     /// Initializes four observations.
-    ObservationTest()
-        :a("alpha", static_cast<int64_t>(1234)), // integer
-         b("beta", 12.34), // float
-         c("gamma", millisec::time_duration(1,2,3,4)), // duration
-         d("delta", "1234") { // string
+    ObservationTest() :
+        a("alpha", static_cast<int64_t>(1234)), // integer
+        b("beta", 12.34), // float
+        c("gamma", millisec::time_duration(1, 2, 3, 4)), // duration
+        d("delta", "1234") { // string
     }
 
     Observation a;
@@ -49,7 +49,6 @@ public:
 // Basic tests for the Observation 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());
     EXPECT_EQ(Observation::STAT_FLOAT, b.getType());
     EXPECT_EQ(Observation::STAT_DURATION, c.getType());
@@ -57,7 +56,7 @@ TEST_F(ObservationTest, constructor) {
 
     EXPECT_EQ(1234, a.getInteger().first);
     EXPECT_EQ(12.34, b.getFloat().first);
-    EXPECT_EQ(millisec::time_duration(1,2,3,4),
+    EXPECT_EQ(millisec::time_duration(1, 2, 3, 4),
               c.getDuration().first);
     EXPECT_EQ("1234", d.getString().first);
 
@@ -83,27 +82,25 @@ TEST_F(ObservationTest, constructor) {
 // This test checks whether it is possible to set to an absolute value for all
 // given types.
 TEST_F(ObservationTest, setValue) {
-
     EXPECT_NO_THROW(a.setValue(static_cast<int64_t>(5678)));
     EXPECT_NO_THROW(b.setValue(56e+78));
-    EXPECT_NO_THROW(c.setValue(millisec::time_duration(5,6,7,8)));
+    EXPECT_NO_THROW(c.setValue(millisec::time_duration(5, 6, 7, 8)));
     EXPECT_NO_THROW(d.setValue("fiveSixSevenEight"));
 
 
     EXPECT_EQ(5678, a.getInteger().first);
     EXPECT_EQ(56e+78, b.getFloat().first);
-    EXPECT_EQ(millisec::time_duration(5,6,7,8),
-              c.getDuration().first);
+    EXPECT_EQ(millisec::time_duration(5, 6, 7, 8), c.getDuration().first);
     EXPECT_EQ("fiveSixSevenEight", d.getString().first);
 
     // Now check whether setting value to a different type does
     // throw an exception
     EXPECT_THROW(a.setValue(56e+78), InvalidStatType);
-    EXPECT_THROW(a.setValue(millisec::time_duration(5,6,7,8)), InvalidStatType);
+    EXPECT_THROW(a.setValue(millisec::time_duration(5, 6, 7, 8)), InvalidStatType);
     EXPECT_THROW(a.setValue("fiveSixSevenEight"), InvalidStatType);
 
     EXPECT_THROW(b.setValue(static_cast<int64_t>(5678)), InvalidStatType);
-    EXPECT_THROW(b.setValue(millisec::time_duration(5,6,7,8)), InvalidStatType);
+    EXPECT_THROW(b.setValue(millisec::time_duration(5, 6, 7, 8)), InvalidStatType);
     EXPECT_THROW(b.setValue("fiveSixSevenEight"), InvalidStatType);
 
     EXPECT_THROW(c.setValue(static_cast<int64_t>(5678)), InvalidStatType);
@@ -112,26 +109,325 @@ TEST_F(ObservationTest, setValue) {
 
     EXPECT_THROW(d.setValue(static_cast<int64_t>(5678)), InvalidStatType);
     EXPECT_THROW(d.setValue(56e+78), InvalidStatType);
-    EXPECT_THROW(d.setValue(millisec::time_duration(5,6,7,8)), InvalidStatType);
+    EXPECT_THROW(d.setValue(millisec::time_duration(5, 6, 7, 8)), InvalidStatType);
 }
 
 // This test checks whether it is possible to add value to existing
 // counter.
 TEST_F(ObservationTest, addValue) {
-
-    // Note: all Observations were set to 1234,12.34 or similar in
+    // Note: all Observations were set to 1234, 12.34 or similar in
     // ObservationTest constructor.
 
     EXPECT_NO_THROW(a.addValue(static_cast<int64_t>(5678)));
     EXPECT_NO_THROW(b.addValue(56.78));
-    EXPECT_NO_THROW(c.addValue(millisec::time_duration(5,6,7,8)));
+    EXPECT_NO_THROW(c.addValue(millisec::time_duration(5, 6, 7, 8)));
     EXPECT_NO_THROW(d.addValue("fiveSixSevenEight"));
 
     EXPECT_EQ(6912, a.getInteger().first);
     EXPECT_EQ(69.12, b.getFloat().first);
-
-    EXPECT_EQ(millisec::time_duration(6,8,10,12), c.getDuration().first);
+    EXPECT_EQ(millisec::time_duration(6, 8, 10, 12), c.getDuration().first);
     EXPECT_EQ("1234fiveSixSevenEight", d.getString().first);
+
+    ASSERT_EQ(a.getSize(), 2);
+    ASSERT_EQ(b.getSize(), 2);
+    ASSERT_EQ(c.getSize(), 2);
+    ASSERT_EQ(d.getSize(), 2);
+}
+
+// This test checks if collecting more than one sample
+// works well.
+TEST_F(ObservationTest, moreThanOne) {
+    // Arrays of 4 types of samples
+    int64_t int_samples[3] = {1234, 6912, 5678};
+    double float_samples[3] = {12.34, 69.12, 56e+78};
+    millisec::time_duration duration_samples[3] = {millisec::time_duration(1, 2, 3, 4),
+        millisec::time_duration(6, 8, 10, 12), millisec::time_duration(5, 6, 7, 8)};
+    std::string string_samples[3] = {"1234", "1234fiveSixSevenEight", "fiveSixSevenEight"};
+
+    EXPECT_NO_THROW(a.addValue(static_cast<int64_t>(5678)));
+    EXPECT_NO_THROW(b.addValue(56.78));
+    EXPECT_NO_THROW(c.addValue(millisec::time_duration(5, 6, 7, 8)));
+    EXPECT_NO_THROW(d.addValue("fiveSixSevenEight"));
+
+    EXPECT_NO_THROW(a.setValue(static_cast<int64_t>(5678)));
+    EXPECT_NO_THROW(b.setValue(56e+78));
+    EXPECT_NO_THROW(c.setValue(millisec::time_duration(5, 6, 7, 8)));
+    EXPECT_NO_THROW(d.setValue("fiveSixSevenEight"));
+
+    ASSERT_EQ(a.getSize(), 3);
+    ASSERT_EQ(b.getSize(), 3);
+    ASSERT_EQ(c.getSize(), 3);
+    ASSERT_EQ(d.getSize(), 3);
+
+    ASSERT_NO_THROW(a.getIntegers());
+    ASSERT_NO_THROW(b.getFloats());
+    ASSERT_NO_THROW(c.getDurations());
+    ASSERT_NO_THROW(d.getStrings());
+
+    std::list<IntegerSample> samples_int = a.getIntegers(); // List of all integer samples
+    std::list<FloatSample> samples_float = b.getFloats(); // List of all float samples
+    std::list<DurationSample> samples_dur = c.getDurations(); // List of all duration samples
+    std::list<StringSample> samples_str = d.getStrings(); // List of all string samples
+
+    uint32_t i = 2; // Index pointed to the end of array of samples
+
+    for (std::list<IntegerSample>::iterator it = samples_int.begin(); it != samples_int.end(); ++it) {
+        EXPECT_EQ(int_samples[i], static_cast<int64_t>((*it).first));
+        --i;
+    }
+    i = 2;
+    for (std::list<FloatSample>::iterator it = samples_float.begin(); it != samples_float.end(); ++it) {
+        EXPECT_EQ(float_samples[i], (*it).first);
+        --i;
+    }
+    i = 2;
+    for (std::list<DurationSample>::iterator it = samples_dur.begin(); it != samples_dur.end(); ++it) {
+        EXPECT_EQ(duration_samples[i], (*it).first);
+        --i;
+    }
+    i = 2;
+    for (std::list<StringSample>::iterator it = samples_str.begin(); it != samples_str.end(); ++it) {
+        EXPECT_EQ(string_samples[i], (*it).first);
+        --i;
+    }
+}
+
+// This test checks whether the size of storage
+// is equal to the true value
+TEST_F(ObservationTest, getSize) {
+    // Check if size of storages is equal to 1
+    ASSERT_EQ(a.getSize(), 1);
+    ASSERT_EQ(b.getSize(), 1);
+    ASSERT_EQ(c.getSize(), 1);
+    ASSERT_EQ(d.getSize(), 1);
+
+    a.addValue(static_cast<int64_t>(5678));
+    b.addValue(56.78);
+    c.addValue(millisec::time_duration(5, 6, 7, 8));
+    d.addValue("fiveSixSevenEight");
+
+    EXPECT_NO_THROW(a.getSize());
+    EXPECT_NO_THROW(b.getSize());
+    EXPECT_NO_THROW(c.getSize());
+    EXPECT_NO_THROW(d.getSize());
+
+    // Check if size of storages is equal to 2
+    ASSERT_EQ(a.getSize(), 2);
+    ASSERT_EQ(b.getSize(), 2);
+    ASSERT_EQ(c.getSize(), 2);
+    ASSERT_EQ(d.getSize(), 2);
+
+    a.setValue(static_cast<int64_t>(5678));
+    b.setValue(56e+78);
+    c.setValue(millisec::time_duration(5, 6, 7, 8));
+    d.setValue("fiveSixSevenEight");
+
+    EXPECT_NO_THROW(a.getSize());
+    EXPECT_NO_THROW(b.getSize());
+    EXPECT_NO_THROW(c.getSize());
+    EXPECT_NO_THROW(d.getSize());
+
+    // Check if size of storages is equal to 3
+    ASSERT_EQ(a.getSize(), 3);
+    ASSERT_EQ(b.getSize(), 3);
+    ASSERT_EQ(c.getSize(), 3);
+    ASSERT_EQ(d.getSize(), 3);
+}
+
+// Checks whether setting amount limits works properly
+TEST_F(ObservationTest, setCountLimit) {
+    // Preparing of 21 test's samples for each type of storage
+    int64_t int_samples[22] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+           14, 15, 16, 17, 18, 19, 20, 21};
+    double float_samples[22] = {0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0,
+           9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0,
+           20.0, 21.0};
+    std::string string_samples[22] = {"a", "b", "c", "d", "e", "f", "g", "h",
+           "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u",
+           "v"};
+    millisec::time_duration duration_samples[22];
+
+    for (uint32_t i = 0; i < 22; ++i) {
+        duration_samples[i] = millisec::time_duration(0, 0, 0, i);
+    }
+
+    // By default the max_sample_count is set to 20 and max_sample_age
+    // is deactivated
+    // Adding 21 samples to each type of Observation
+    for (uint32_t i = 0; i < 21; ++i) {
+        a.setValue(int_samples[i]);
+    }
+    for (uint32_t i = 0; i < 21; ++i) {
+        b.setValue(float_samples[i]);
+    }
+    for (uint32_t i = 0; i < 21; ++i) {
+        c.setValue(duration_samples[i]);
+    }
+    for (uint32_t i = 0; i < 21; ++i) {
+        d.setValue(string_samples[i]);
+    }
+
+    // Getting all 4 types of samples after inserting 21 values
+    std::list<IntegerSample> samples_int = a.getIntegers();
+    std::list<FloatSample> samples_float = b.getFloats();
+    std::list<DurationSample> samples_duration = c.getDurations();
+    std::list<StringSample> samples_string = d.getStrings();
+
+    // Check if size of storages is equal to 20
+    ASSERT_EQ(a.getSize(), 20);
+    ASSERT_EQ(b.getSize(), 20);
+    ASSERT_EQ(c.getSize(), 20);
+    ASSERT_EQ(d.getSize(), 20);
+
+    // And whether storaged values are correct
+    uint32_t i = 20; // index of the last element in array of test's samples
+    for (std::list<IntegerSample>::iterator it = samples_int.begin(); it != samples_int.end(); ++it) {
+        EXPECT_EQ((*it).first, int_samples[i]);
+        --i;
+    }
+    i = 20; // index of last element in array of test's samples
+    for (std::list<FloatSample>::iterator it = samples_float.begin(); it != samples_float.end(); ++it) {
+        EXPECT_EQ((*it).first, float_samples[i]);
+        --i;
+    }
+    i = 20; // index of last element in array of test's samples
+    for (std::list<DurationSample>::iterator it = samples_duration.begin(); it != samples_duration.end(); ++it) {
+        EXPECT_EQ((*it).first, duration_samples[i]);
+        --i;
+    }
+    i = 20; // index of last element in array of test's samples
+    for (std::list<StringSample>::iterator it = samples_string.begin(); it != samples_string.end(); ++it) {
+        EXPECT_EQ((*it).first, string_samples[i]);
+        --i;
+    }
+
+    // Change size of storage to smaller one
+    ASSERT_NO_THROW(a.setMaxSampleCount(10));
+    ASSERT_NO_THROW(b.setMaxSampleCount(10));
+    ASSERT_NO_THROW(c.setMaxSampleCount(10));
+    ASSERT_NO_THROW(d.setMaxSampleCount(10));
+
+    samples_int = a.getIntegers();
+    samples_float = b.getFloats();
+    samples_duration = c.getDurations();
+    samples_string = d.getStrings();
+
+    // Check if size of storages is equal to 10
+    ASSERT_EQ(a.getSize(), 10);
+    ASSERT_EQ(b.getSize(), 10);
+    ASSERT_EQ(c.getSize(), 10);
+    ASSERT_EQ(d.getSize(), 10);
+
+    // And whether storages contain only the 10 newest values
+    i = 20; // index of last element in array of test's samples
+    for (std::list<IntegerSample>::iterator it = samples_int.begin(); it != samples_int.end(); ++it) {
+        EXPECT_EQ((*it).first, int_samples[i]);
+        --i;
+    }
+    i = 20; // index of last element in array of test's samples
+    for (std::list<FloatSample>::iterator it = samples_float.begin(); it != samples_float.end(); ++it) {
+        EXPECT_EQ((*it).first, float_samples[i]);
+        --i;
+    }
+    i = 20; // index of last element in array of test's samples
+    for (std::list<DurationSample>::iterator it = samples_duration.begin(); it != samples_duration.end(); ++it) {
+        EXPECT_EQ((*it).first, duration_samples[i]);
+        --i;
+    }
+    i = 20; // index of last element in array of test's samples
+    for (std::list<StringSample>::iterator it = samples_string.begin(); it != samples_string.end(); ++it) {
+        EXPECT_EQ((*it).first, string_samples[i]);
+        --i;
+    }
+
+    // Resize max_sample_count to greater
+    ASSERT_NO_THROW(a.setMaxSampleCount(50));
+    ASSERT_NO_THROW(b.setMaxSampleCount(50));
+    ASSERT_NO_THROW(c.setMaxSampleCount(50));
+    ASSERT_NO_THROW(d.setMaxSampleCount(50));
+
+    // Check if size of storages did not change without adding new value
+    ASSERT_EQ(a.getSize(), 10);
+    ASSERT_EQ(b.getSize(), 10);
+    ASSERT_EQ(c.getSize(), 10);
+    ASSERT_EQ(d.getSize(), 10);
+
+    // Add new values to each type of Observation
+    a.setValue(static_cast<int64_t>(21));
+    b.setValue(21.0);
+    c.setValue(millisec::time_duration(0, 0, 0, 21));
+    d.setValue("v");
+
+    samples_int = a.getIntegers();
+    samples_float = b.getFloats();
+    samples_duration = c.getDurations();
+    samples_string = d.getStrings();
+
+    ASSERT_EQ(a.getSize(), 11);
+    ASSERT_EQ(b.getSize(), 11);
+    ASSERT_EQ(c.getSize(), 11);
+    ASSERT_EQ(d.getSize(), 11);
+
+    i = 21; // index of last element in array of test's samples
+    for (std::list<IntegerSample>::iterator it = samples_int.begin(); it != samples_int.end(); ++it) {
+        EXPECT_EQ((*it).first, int_samples[i]);
+        --i;
+    }
+    i = 21; // index of last element in array of test's samples
+    for (std::list<FloatSample>::iterator it = samples_float.begin(); it != samples_float.end(); ++it) {
+        EXPECT_EQ((*it).first, float_samples[i]);
+        --i;
+    }
+    i = 21; // index of last element in array of test's samples
+    for (std::list<DurationSample>::iterator it = samples_duration.begin(); it != samples_duration.end(); ++it) {
+        EXPECT_EQ((*it).first, duration_samples[i]);
+        --i;
+    }
+    i = 21; // index of last element in array of test's samples
+    for (std::list<StringSample>::iterator it = samples_string.begin(); it != samples_string.end(); ++it) {
+        EXPECT_EQ((*it).first, string_samples[i]);
+        --i;
+    }
+
+}
+
+// Checks whether setting age limits works properly
+TEST_F(ObservationTest, setAgeLimit) {
+    // Set max_sample_age to 1 second
+    ASSERT_NO_THROW(c.setMaxSampleAge(millisec::time_duration(0, 0, 1, 0)));
+    // Add some value
+    c.setValue(millisec::time_duration(0, 0, 0, 5));
+    // Wait 1 second
+    sleep(1);
+    // and add new value
+    c.setValue(millisec::time_duration(0, 0, 0, 3));
+
+    // get the list of all samples
+    std::list<DurationSample> samples_duration = c.getDurations();
+    // check whether the size of samples is equal to 1
+    ASSERT_EQ(c.getSize(), 1);
+    // and whether it contains an expected value
+    EXPECT_EQ((*samples_duration.begin()).first, millisec::time_duration(0, 0, 0, 3));
+
+    // Wait 1 second to ensure removing previously set value
+    sleep(1);
+    // add 10 new values
+    for (uint32_t i = 0; i < 10; ++i) {
+        c.setValue(millisec::time_duration(0, 0, 0, i));
+    }
+    // change the max_sample_age to smaller
+    ASSERT_NO_THROW(c.setMaxSampleAge(millisec::time_duration(0, 0, 0, 300)));
+
+    samples_duration = c.getDurations();
+    // check whether the size of samples is equal to 10
+    ASSERT_EQ(c.getSize(), 10);
+
+    // and whether it contains expected values
+    uint32_t i = 9;
+    for (std::list<DurationSample>::iterator it = samples_duration.begin(); it != samples_duration.end(); ++it) {
+        EXPECT_EQ((*it).first, millisec::time_duration(0, 0, 0, i));
+        --i;
+    }
 }
 
 // Test checks whether timing is reported properly.
@@ -149,7 +445,7 @@ TEST_F(ObservationTest, timers) {
 
     FloatSample sample = b.getFloat();
 
-    // Let's check that the timestamp is within (before,after) range:
+    // 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);
@@ -159,11 +455,14 @@ TEST_F(ObservationTest, timers) {
 // See https://gitlab.isc.org/isc-projects/kea/wikis/designs/Stats-design
 /// for details.
 TEST_F(ObservationTest, integerToJSON) {
+    // String which contains first added sample
+    std::string first_sample = ", 1234, \"" +
+        isc::util::ptimeToText(a.getInteger().second) + "\" ] ]";
 
     a.setValue(static_cast<int64_t>(1234));
 
-    std::string exp = "[ [ 1234, \""
-        + isc::util::ptimeToText(a.getInteger().second) + "\" ] ]";
+    std::string exp = "[ [ 1234, \"" +
+        isc::util::ptimeToText(a.getInteger().second) + "\"" + first_sample;
 
     std::cout << a.getJSON()->str() << std::endl;
     EXPECT_EQ(exp, a.getJSON()->str());
@@ -174,13 +473,17 @@ TEST_F(ObservationTest, integerToJSON) {
 /// https://gitlab.isc.org/isc-projects/kea/wikis/designs/Stats-design
 /// for details.
 TEST_F(ObservationTest, floatToJSON) {
+    // String which contains first added sample
+    std::string first_sample = ", 12.34, \"" +
+        isc::util::ptimeToText(b.getFloat().second) + "\" ] ]";
 
     // Let's use a value that converts easily to floating point.
     // No need to deal with infinite fractions in binary systems.
+
     b.setValue(1234.5);
 
-    std::string exp = "[ [ 1234.5, \""
-        + isc::util::ptimeToText(b.getFloat().second) + "\" ] ]";
+    std::string exp = "[ [ 1234.5, \"" +
+        isc::util::ptimeToText(b.getFloat().second) + "\"" + first_sample;
 
     std::cout << b.getJSON()->str() << std::endl;
     EXPECT_EQ(exp, b.getJSON()->str());
@@ -191,11 +494,15 @@ TEST_F(ObservationTest, floatToJSON) {
 // details.
 TEST_F(ObservationTest, durationToJSON) {
 
+    // String which contains first added sample
+    std::string first_sample = ", \"01:02:03.000004\", \"" +
+        isc::util::ptimeToText(c.getDuration().second) + "\" ] ]";
+
     // 1 hour 2 minutes 3 seconds and 4 milliseconds
-    c.setValue(time_duration(1,2,3,4));
+    c.setValue(time_duration(1, 2, 3, 4));
 
-    std::string exp = "[ [ \"01:02:03.000004\", \""
-        + isc::util::ptimeToText(c.getDuration().second) + "\" ] ]";
+    std::string exp = "[ [ \"01:02:03.000004\", \"" +
+        isc::util::ptimeToText(c.getDuration().second) + "\"" + first_sample;
 
     std::cout << c.getJSON()->str() << std::endl;
     EXPECT_EQ(exp, c.getJSON()->str());
@@ -205,12 +512,13 @@ TEST_F(ObservationTest, durationToJSON) {
 // See https://gitlab.isc.org/isc-projects/kea/wikis/designs/Stats-design
 // for details.
 TEST_F(ObservationTest, stringToJSON) {
-
-    //
+    // String which contains first added sample
+    std::string first_sample = ", \"1234\", \"" +
+        isc::util::ptimeToText(d.getString().second) + "\" ] ]";
     d.setValue("Lorem ipsum dolor sit amet");
 
-    std::string exp = "[ [ \"Lorem ipsum dolor sit amet\", \""
-        + isc::util::ptimeToText(d.getString().second) + "\" ] ]";
+    std::string exp = "[ [ \"Lorem ipsum dolor sit amet\", \"" +
+        isc::util::ptimeToText(d.getString().second) + "\"" + first_sample;
 
     std::cout << d.getJSON()->str() << std::endl;
     EXPECT_EQ(exp, d.getJSON()->str());
@@ -218,6 +526,11 @@ TEST_F(ObservationTest, stringToJSON) {
 
 // Checks whether reset() resets the statistics properly.
 TEST_F(ObservationTest, reset) {
+    EXPECT_NO_THROW(a.addValue(static_cast<int64_t>(5678)));
+    EXPECT_NO_THROW(b.addValue(56.78));
+    EXPECT_NO_THROW(c.addValue(millisec::time_duration(5, 6, 7, 8)));
+    EXPECT_NO_THROW(d.addValue("fiveSixSevenEight"));
+
     a.reset(); // integer
     b.reset(); // float
     c.reset(); // duration
@@ -225,8 +538,13 @@ TEST_F(ObservationTest, reset) {
 
     EXPECT_EQ(0, a.getInteger().first);
     EXPECT_EQ(0.0, b.getFloat().first);
-    EXPECT_EQ(time_duration(0,0,0,0), c.getDuration().first);
+    EXPECT_EQ(time_duration(0, 0, 0, 0), c.getDuration().first);
     EXPECT_EQ("", d.getString().first);
+
+    ASSERT_EQ(a.getSize(), 1);
+    ASSERT_EQ(b.getSize(), 1);
+    ASSERT_EQ(c.getSize(), 1);
+    ASSERT_EQ(d.getSize(), 1);
 }
 
 // Checks whether an observation can keep its name.
index 782cb7c5095dd02fda6adf40dfd6ec67532905f3..29f9cc9e5724cd9205b2e585fa5682bccf198947 100644 (file)
@@ -48,7 +48,6 @@ public:
 
 // Basic test for statistics manager interface.
 TEST_F(StatsMgrTest, basic) {
-
     // Getting an instance
     EXPECT_NO_THROW(StatsMgr::instance());
 
@@ -66,8 +65,8 @@ TEST_F(StatsMgrTest, integerStat) {
     EXPECT_NO_THROW(alpha = StatsMgr::instance().getObservation("alpha"));
     ASSERT_TRUE(alpha);
 
-    std::string exp = "{ \"alpha\": [ [ 1234, \""
-        isc::util::ptimeToText(alpha->getInteger().second) + "\" ] ] }";
+    std::string exp = "{ \"alpha\": [ [ 1234, \"" +
+        isc::util::ptimeToText(alpha->getInteger().second) + "\" ] ] }";
 
     EXPECT_EQ(exp, StatsMgr::instance().get("alpha")->str());
 }
@@ -81,8 +80,8 @@ TEST_F(StatsMgrTest, floatStat) {
     EXPECT_NO_THROW(beta = StatsMgr::instance().getObservation("beta"));
     ASSERT_TRUE(beta);
 
-    std::string exp = "{ \"beta\": [ [ 12.34, \""
-        isc::util::ptimeToText(beta->getFloat().second) + "\" ] ] }";
+    std::string exp = "{ \"beta\": [ [ 12.34, \"" +
+        isc::util::ptimeToText(beta->getFloat().second) + "\" ] ] }";
 
     EXPECT_EQ(exp, StatsMgr::instance().get("beta")->str());
 }
@@ -91,14 +90,14 @@ TEST_F(StatsMgrTest, floatStat) {
 // a duration statistic.
 TEST_F(StatsMgrTest, durationStat) {
     EXPECT_NO_THROW(StatsMgr::instance().setValue("gamma",
-                                                  microsec::time_duration(1,2,3,4)));
+                                                  microsec::time_duration(1, 2, 3, 4)));
 
     ObservationPtr gamma;
     EXPECT_NO_THROW(gamma = StatsMgr::instance().getObservation("gamma"));
     ASSERT_TRUE(gamma);
 
-    std::string exp = "{ \"gamma\": [ [ \"01:02:03.000004\", \""
-        isc::util::ptimeToText(gamma->getDuration().second) + "\" ] ] }";
+    std::string exp = "{ \"gamma\": [ [ \"01:02:03.000004\", \"" +
+        isc::util::ptimeToText(gamma->getDuration().second) + "\" ] ] }";
 
     EXPECT_EQ(exp, StatsMgr::instance().get("gamma")->str());
 }
@@ -113,37 +112,82 @@ TEST_F(StatsMgrTest, stringStat) {
     EXPECT_NO_THROW(delta = StatsMgr::instance().getObservation("delta"));
     ASSERT_TRUE(delta);
 
-    std::string exp = "{ \"delta\": [ [ \"Lorem ipsum\", \""
-        isc::util::ptimeToText(delta->getString().second) + "\" ] ] }";
+    std::string exp = "{ \"delta\": [ [ \"Lorem ipsum\", \"" +
+        isc::util::ptimeToText(delta->getString().second) + "\" ] ] }";
 
     EXPECT_EQ(exp, StatsMgr::instance().get("delta")->str());
 }
 
-// Setting limits is currently not implemented, so those methods should
-// throw.
+// Basic test of getSize function.
+TEST_F(StatsMgrTest, getSize) {
+    StatsMgr::instance().setValue("alpha", static_cast<int64_t>(1234));
+    StatsMgr::instance().setValue("beta", 12.34);
+    StatsMgr::instance().setValue("gamma", microsec::time_duration(1, 2, 3, 4));
+    StatsMgr::instance().setValue("delta", "Lorem ipsum");
+
+    EXPECT_NO_THROW(StatsMgr::instance().getSize("alpha"));
+    EXPECT_NO_THROW(StatsMgr::instance().getSize("beta"));
+    EXPECT_NO_THROW(StatsMgr::instance().getSize("gamma"));
+    EXPECT_NO_THROW(StatsMgr::instance().getSize("delta"));
+
+    EXPECT_EQ(StatsMgr::instance().getSize("alpha"), 1);
+    EXPECT_EQ(StatsMgr::instance().getSize("beta"), 1);
+    EXPECT_EQ(StatsMgr::instance().getSize("gamma"), 1);
+    EXPECT_EQ(StatsMgr::instance().getSize("delta"), 1);
+}
+
+// Test checks whether setting age limit and count limit works properly
 TEST_F(StatsMgrTest, setLimits) {
-    EXPECT_THROW(StatsMgr::instance().setMaxSampleAge("foo",
-                                                      time_duration(1,0,0,0)),
-                 NotImplemented);
+    // Initializing of an integer type observation
+    StatsMgr::instance().setValue("foo", static_cast<int64_t>(1));
+
+    EXPECT_NO_THROW(StatsMgr::instance().setMaxSampleAge("foo",
+                                                         time_duration(0, 0, 1, 0)));
 
-    EXPECT_THROW(StatsMgr::instance().setMaxSampleCount("foo", 100),
-                 NotImplemented);
+    for (uint32_t i = 0; i < 10; ++i) {
+        if (i == 5) {
+            sleep(1); // wait one second to force exceeding the time limit
+        }
+        StatsMgr::instance().setValue("foo", static_cast<int64_t>(i));
+    }
+
+    EXPECT_EQ(StatsMgr::instance().getSize("foo"), 5);
+    EXPECT_NO_THROW(StatsMgr::instance().setMaxSampleCount("foo", 100));
+
+    for (int64_t i = 0; i < 200; ++i) {
+        StatsMgr::instance().setValue("foo", i);
+    }
+
+    EXPECT_EQ(StatsMgr::instance().getSize("foo"), 100);
 }
 
 // This test checks whether a single (get("foo")) and all (getAll())
 // statistics are reported properly.
 TEST_F(StatsMgrTest, getGetAll) {
-
     // Set a couple of statistics
     StatsMgr::instance().setValue("alpha", static_cast<int64_t>(1234));
     StatsMgr::instance().setValue("beta", 12.34);
-    StatsMgr::instance().setValue("gamma", time_duration(1,2,3,4));
+    StatsMgr::instance().setValue("gamma", time_duration(1, 2, 3, 4));
     StatsMgr::instance().setValue("delta", "Lorem");
 
+    // The string's representation of firstly added statistics
+    std::string alpha_first = ", 1234, \"" +
+        isc::util::ptimeToText(StatsMgr::instance().getObservation("alpha")
+                                   ->getInteger().second) + "\" ] ]";
+    std::string beta_first = ", 12.34, \"" +
+        isc::util::ptimeToText(StatsMgr::instance().getObservation("beta")
+                                   ->getFloat().second) + "\" ] ]";
+    std::string gamma_first = ", \"01:02:03.000004\", \"" +
+        isc::util::ptimeToText(StatsMgr::instance().getObservation("gamma")
+                                   ->getDuration().second) + "\" ] ]";
+    std::string delta_first = ", \"Lorem\", \"" +
+        isc::util::ptimeToText(StatsMgr::instance().getObservation("delta")
+                                   ->getString().second) + "\" ] ]";
+
     // Now add some values to them
     StatsMgr::instance().addValue("alpha", static_cast<int64_t>(5678));
     StatsMgr::instance().addValue("beta", 56.78);
-    StatsMgr::instance().addValue("gamma", time_duration(5,6,7,8));
+    StatsMgr::instance().addValue("gamma", time_duration(5, 6, 7, 8));
     StatsMgr::instance().addValue("delta", " ipsum");
 
     // There should be 4 statistics reported
@@ -160,18 +204,18 @@ TEST_F(StatsMgrTest, getGetAll) {
     ASSERT_TRUE(rep_gamma);
     ASSERT_TRUE(rep_delta);
 
-    std::string exp_str_alpha = "[ [ 6912, \""
-        isc::util::ptimeToText(StatsMgr::instance().getObservation("alpha")
-                                   ->getInteger().second) + "\" ] ]";
-    std::string exp_str_beta = "[ [ 69.12, \""
-        isc::util::ptimeToText(StatsMgr::instance().getObservation("beta")
-                                   ->getFloat().second) + "\" ] ]";
-    std::string exp_str_gamma = "[ [ \"06:08:10.000012\", \""
-        isc::util::ptimeToText(StatsMgr::instance().getObservation("gamma")
-                                   ->getDuration().second) + "\" ] ]";
-    std::string exp_str_delta = "[ [ \"Lorem ipsum\", \""
-        isc::util::ptimeToText(StatsMgr::instance().getObservation("delta")
-                                   ->getString().second) + "\" ] ]";
+    std::string exp_str_alpha = "[ [ 6912, \"" +
+        isc::util::ptimeToText(StatsMgr::instance().getObservation("alpha")
+                                   ->getInteger().second) + "\"" + alpha_first;
+    std::string exp_str_beta = "[ [ 69.12, \"" +
+        isc::util::ptimeToText(StatsMgr::instance().getObservation("beta")
+                                   ->getFloat().second) + "\"" + beta_first;
+    std::string exp_str_gamma = "[ [ \"06:08:10.000012\", \"" +
+        isc::util::ptimeToText(StatsMgr::instance().getObservation("gamma")
+                                   ->getDuration().second) + "\"" + gamma_first;
+    std::string exp_str_delta = "[ [ \"Lorem ipsum\", \"" +
+        isc::util::ptimeToText(StatsMgr::instance().getObservation("delta")
+                                   ->getString().second) + "\"" + delta_first;
 
     // Check that individual stats are reported properly
     EXPECT_EQ("{ \"alpha\": " + exp_str_alpha + " }", rep_alpha->str());
@@ -203,21 +247,21 @@ TEST_F(StatsMgrTest, getGetAll) {
 
 // This test checks whether existing statistics can be reset.
 TEST_F(StatsMgrTest, reset) {
-
     // Set a couple of statistics
     StatsMgr::instance().setValue("alpha", static_cast<int64_t>(1234));
     StatsMgr::instance().setValue("beta", 12.34);
-    StatsMgr::instance().setValue("gamma", time_duration(1,2,3,4));
+    StatsMgr::instance().setValue("gamma", time_duration(1, 2, 3, 4));
     StatsMgr::instance().setValue("delta", "Lorem ipsum");
 
     // This should reset alpha to 0
     EXPECT_NO_THROW(StatsMgr::instance().reset("alpha"));
-    EXPECT_EQ(0, StatsMgr::instance().getObservation("alpha")->getInteger().first);
+    EXPECT_EQ(0,
+              StatsMgr::instance().getObservation("alpha")->getInteger().first);
 
     // The other stats should remain untouched
     EXPECT_EQ(12.34,
               StatsMgr::instance().getObservation("beta")->getFloat().first);
-    EXPECT_EQ(time_duration(1,2,3,4),
+    EXPECT_EQ(time_duration(1, 2, 3, 4),
               StatsMgr::instance().getObservation("gamma")->getDuration().first);
     EXPECT_EQ("Lorem ipsum",
               StatsMgr::instance().getObservation("delta")->getString().first);
@@ -228,7 +272,7 @@ TEST_F(StatsMgrTest, reset) {
     EXPECT_NO_THROW(StatsMgr::instance().reset("delta"));
     EXPECT_EQ(0.0,
               StatsMgr::instance().getObservation("beta")->getFloat().first);
-    EXPECT_EQ(time_duration(0,0,0,0),
+    EXPECT_EQ(time_duration(0, 0, 0, 0),
               StatsMgr::instance().getObservation("gamma")->getDuration().first);
     EXPECT_EQ("",
               StatsMgr::instance().getObservation("delta")->getString().first);
@@ -239,19 +283,19 @@ TEST_F(StatsMgrTest, reset) {
 
 // This test checks whether existing statistics can be reset.
 TEST_F(StatsMgrTest, resetAll) {
-
     // Set a couple of statistics
     StatsMgr::instance().setValue("alpha", static_cast<int64_t>(1234));
     StatsMgr::instance().setValue("beta", 12.34);
-    StatsMgr::instance().setValue("gamma", time_duration(1,2,3,4));
+    StatsMgr::instance().setValue("gamma", time_duration(1, 2, 3, 4));
     StatsMgr::instance().setValue("delta", "Lorem ipsum");
 
     // This should reset alpha to 0
     EXPECT_NO_THROW(StatsMgr::instance().resetAll());
-    EXPECT_EQ(0, StatsMgr::instance().getObservation("alpha")->getInteger().first);
+    EXPECT_EQ(0,
+              StatsMgr::instance().getObservation("alpha")->getInteger().first);
     EXPECT_EQ(0.0,
               StatsMgr::instance().getObservation("beta")->getFloat().first);
-    EXPECT_EQ(time_duration(0,0,0,0),
+    EXPECT_EQ(time_duration(0, 0, 0, 0),
               StatsMgr::instance().getObservation("gamma")->getDuration().first);
     EXPECT_EQ("",
               StatsMgr::instance().getObservation("delta")->getString().first);
@@ -262,11 +306,10 @@ TEST_F(StatsMgrTest, resetAll) {
 
 // This test checks whether statistics can be removed.
 TEST_F(StatsMgrTest, removeAll) {
-
     // Set a couple of statistics
     StatsMgr::instance().setValue("alpha", static_cast<int64_t>(1234));
     StatsMgr::instance().setValue("beta", 12.34);
-    StatsMgr::instance().setValue("gamma", time_duration(1,2,3,4));
+    StatsMgr::instance().setValue("gamma", time_duration(1, 2, 3, 4));
     StatsMgr::instance().setValue("delta", "Lorem ipsum");
 
     // This should reset alpha to 0
@@ -300,7 +343,7 @@ TEST_F(StatsMgrTest, DISABLED_performanceSingleAdd) {
 
     ptime before = microsec_clock::local_time();
     for (uint32_t i = 0; i < cycles; ++i) {
-        StatsMgr::instance().addValue("metric1", 0.1*i);
+        StatsMgr::instance().addValue("metric1", 0.1 * i);
     }
     ptime after = microsec_clock::local_time();
 
@@ -322,7 +365,7 @@ TEST_F(StatsMgrTest, DISABLED_performanceSingleSet) {
 
     ptime before = microsec_clock::local_time();
     for (uint32_t i = 0; i < cycles; ++i) {
-        StatsMgr::instance().setValue("metric1", 0.1*i);
+        StatsMgr::instance().setValue("metric1", 0.1 * i);
     }
     ptime after = microsec_clock::local_time();
 
@@ -422,8 +465,8 @@ TEST_F(StatsMgrTest, commandStatisticGet) {
     EXPECT_NO_THROW(alpha = StatsMgr::instance().getObservation("alpha"));
     ASSERT_TRUE(alpha);
 
-    std::string exp = "{ \"alpha\": [ [ 1234, \""
-        isc::util::ptimeToText(alpha->getInteger().second) + "\" ] ] }";
+    std::string exp = "{ \"alpha\": [ [ 1234, \"" +
+        isc::util::ptimeToText(alpha->getInteger().second) + "\" ] ] }";
 
     EXPECT_EQ("{ \"arguments\": " + exp + ", \"result\": 0 }", rsp->str());
 }
@@ -433,7 +476,6 @@ TEST_F(StatsMgrTest, commandStatisticGet) {
 // - a request with missing statistic name
 // - a request for non-existing statistic.
 TEST_F(StatsMgrTest, commandStatisticGetNegative) {
-
     // Case 1: a request without parameters
     ConstElementPtr rsp = StatsMgr::instance().statisticGetHandler("statistic-get",
                                                                    ElementPtr());
@@ -456,11 +498,10 @@ TEST_F(StatsMgrTest, commandStatisticGetNegative) {
 // This test checks whether statistic-get-all command returns all statistics
 // correctly.
 TEST_F(StatsMgrTest, commandGetAll) {
-
     // Set a couple of statistics
     StatsMgr::instance().setValue("alpha", static_cast<int64_t>(1234));
     StatsMgr::instance().setValue("beta", 12.34);
-    StatsMgr::instance().setValue("gamma", time_duration(1,2,3,4));
+    StatsMgr::instance().setValue("gamma", time_duration(1, 2, 3, 4));
     StatsMgr::instance().setValue("delta", "Lorem ipsum");
 
     // Now get them. They're used to generate expected output
@@ -474,17 +515,17 @@ TEST_F(StatsMgrTest, commandGetAll) {
     ASSERT_TRUE(rep_gamma);
     ASSERT_TRUE(rep_delta);
 
-    std::string exp_str_alpha = "[ [ 1234, \""
-        isc::util::ptimeToText(StatsMgr::instance().getObservation("alpha")
+    std::string exp_str_alpha = "[ [ 1234, \"" +
+        isc::util::ptimeToText(StatsMgr::instance().getObservation("alpha")
                                    ->getInteger().second) + "\" ] ]";
-    std::string exp_str_beta = "[ [ 12.34, \""
-        isc::util::ptimeToText(StatsMgr::instance().getObservation("beta")
+    std::string exp_str_beta = "[ [ 12.34, \"" +
+        isc::util::ptimeToText(StatsMgr::instance().getObservation("beta")
                                    ->getFloat().second) + "\" ] ]";
-    std::string exp_str_gamma = "[ [ \"01:02:03.000004\", \""
-        isc::util::ptimeToText(StatsMgr::instance().getObservation("gamma")
+    std::string exp_str_gamma = "[ [ \"01:02:03.000004\", \"" +
+        isc::util::ptimeToText(StatsMgr::instance().getObservation("gamma")
                                    ->getDuration().second) + "\" ] ]";
-    std::string exp_str_delta = "[ [ \"Lorem ipsum\", \""
-        isc::util::ptimeToText(StatsMgr::instance().getObservation("delta")
+    std::string exp_str_delta = "[ [ \"Lorem ipsum\", \"" +
+        isc::util::ptimeToText(StatsMgr::instance().getObservation("delta")
                                    ->getString().second) + "\" ] ]";
 
     // Check that all of them can be reported at once
@@ -537,7 +578,6 @@ TEST_F(StatsMgrTest, commandStatisticReset) {
 // - a request with missing statistic name
 // - a request for non-existing statistic.
 TEST_F(StatsMgrTest, commandStatisticResetNegative) {
-
     // Case 1: a request without parameters
     ConstElementPtr rsp =
         StatsMgr::instance().statisticResetHandler("statistic-reset", ElementPtr());
@@ -561,11 +601,10 @@ TEST_F(StatsMgrTest, commandStatisticResetNegative) {
 // This test checks whether statistic-reset-all command really resets all
 // statistics correctly.
 TEST_F(StatsMgrTest, commandResetAll) {
-
     // Set a couple of statistics
     StatsMgr::instance().setValue("alpha", static_cast<int64_t>(1234));
     StatsMgr::instance().setValue("beta", 12.34);
-    StatsMgr::instance().setValue("gamma", time_duration(1,2,3,4));
+    StatsMgr::instance().setValue("gamma", time_duration(1, 2, 3, 4));
     StatsMgr::instance().setValue("delta", "Lorem ipsum");
 
     // Now get them. They're used to generate expected output
@@ -579,17 +618,17 @@ TEST_F(StatsMgrTest, commandResetAll) {
     ASSERT_TRUE(rep_gamma);
     ASSERT_TRUE(rep_delta);
 
-    std::string exp_str_alpha = "[ [ 1234, \""
-        isc::util::ptimeToText(StatsMgr::instance().getObservation("alpha")
+    std::string exp_str_alpha = "[ [ 1234, \"" +
+        isc::util::ptimeToText(StatsMgr::instance().getObservation("alpha")
                                    ->getInteger().second) + "\" ] ]";
-    std::string exp_str_beta = "[ [ 12.34, \""
-        isc::util::ptimeToText(StatsMgr::instance().getObservation("beta")
+    std::string exp_str_beta = "[ [ 12.34, \"" +
+        isc::util::ptimeToText(StatsMgr::instance().getObservation("beta")
                                    ->getFloat().second) + "\" ] ]";
-    std::string exp_str_gamma = "[ [ \"01:02:03.000004\", \""
-        isc::util::ptimeToText(StatsMgr::instance().getObservation("gamma")
+    std::string exp_str_gamma = "[ [ \"01:02:03.000004\", \"" +
+        isc::util::ptimeToText(StatsMgr::instance().getObservation("gamma")
                                    ->getDuration().second) + "\" ] ]";
-    std::string exp_str_delta = "[ [ \"Lorem ipsum\", \""
-        isc::util::ptimeToText(StatsMgr::instance().getObservation("delta")
+    std::string exp_str_delta = "[ [ \"Lorem ipsum\", \"" +
+        isc::util::ptimeToText(StatsMgr::instance().getObservation("delta")
                                    ->getString().second) + "\" ] ]";
 
     // Check that all of them can be reset at once
@@ -602,10 +641,11 @@ TEST_F(StatsMgrTest, commandResetAll) {
     ASSERT_TRUE(rep_all);
 
     // Check that they're indeed reset
-    EXPECT_EQ(0, StatsMgr::instance().getObservation("alpha")->getInteger().first);
+    EXPECT_EQ(0,
+              StatsMgr::instance().getObservation("alpha")->getInteger().first);
     EXPECT_EQ(0.0f,
               StatsMgr::instance().getObservation("beta")->getFloat().first);
-    EXPECT_EQ(time_duration(0,0,0,0),
+    EXPECT_EQ(time_duration(0, 0, 0, 0),
               StatsMgr::instance().getObservation("gamma")->getDuration().first);
     EXPECT_EQ("",
               StatsMgr::instance().getObservation("delta")->getString().first);
@@ -626,6 +666,7 @@ TEST_F(StatsMgrTest, commandStatisticRemove) {
 
     // It should be gone.
     EXPECT_FALSE(StatsMgr::instance().getObservation("alpha"));
+    EXPECT_EQ(0, StatsMgr::instance().count());
 }
 
 // Test checks if statistic-remove is able to handle:
@@ -633,7 +674,6 @@ TEST_F(StatsMgrTest, commandStatisticRemove) {
 // - a request with missing statistic name
 // - a request for non-existing statistic.
 TEST_F(StatsMgrTest, commandStatisticRemoveNegative) {
-
     // Case 1: a request without parameters
     ConstElementPtr rsp =
         StatsMgr::instance().statisticRemoveHandler("statistic-remove", ElementPtr());
@@ -657,11 +697,10 @@ TEST_F(StatsMgrTest, commandStatisticRemoveNegative) {
 // This test checks whether statistic-remove-all command really resets all
 // statistics correctly.
 TEST_F(StatsMgrTest, commandRemoveAll) {
-
     // Set a couple of statistics
     StatsMgr::instance().setValue("alpha", static_cast<int64_t>(1234));
     StatsMgr::instance().setValue("beta", 12.34);
-    StatsMgr::instance().setValue("gamma", time_duration(1,2,3,4));
+    StatsMgr::instance().setValue("gamma", time_duration(1, 2, 3, 4));
     StatsMgr::instance().setValue("delta", "Lorem ipsum");
 
     // Check that all of them can be reset at once
@@ -676,6 +715,7 @@ TEST_F(StatsMgrTest, commandRemoveAll) {
     EXPECT_FALSE(StatsMgr::instance().getObservation("beta"));
     EXPECT_FALSE(StatsMgr::instance().getObservation("gamma"));
     EXPECT_FALSE(StatsMgr::instance().getObservation("delta"));
+    EXPECT_EQ(0, StatsMgr::instance().count());
 }
 
 };