diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/bin/dhcp4/tests/shared_network_unittest.cc | 3 | ||||
-rw-r--r-- | src/bin/dhcp6/tests/shared_network_unittest.cc | 3 | ||||
-rw-r--r-- | src/lib/stats/observation.cc | 280 | ||||
-rw-r--r-- | src/lib/stats/observation.h | 128 | ||||
-rw-r--r-- | src/lib/stats/stats_mgr.cc | 39 | ||||
-rw-r--r-- | src/lib/stats/stats_mgr.h | 24 | ||||
-rw-r--r-- | src/lib/stats/tests/observation_unittest.cc | 382 | ||||
-rw-r--r-- | src/lib/stats/tests/stats_mgr_unittest.cc | 188 |
8 files changed, 886 insertions, 161 deletions
diff --git a/src/bin/dhcp4/tests/shared_network_unittest.cc b/src/bin/dhcp4/tests/shared_network_unittest.cc index 247b13cead..2a6720b42c 100644 --- a/src/bin/dhcp4/tests/shared_network_unittest.cc +++ b/src/bin/dhcp4/tests/shared_network_unittest.cc @@ -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()); diff --git a/src/bin/dhcp6/tests/shared_network_unittest.cc b/src/bin/dhcp6/tests/shared_network_unittest.cc index c31ec68b8a..87c403d136 100644 --- a/src/bin/dhcp6/tests/shared_network_unittest.cc +++ b/src/bin/dhcp6/tests/shared_network_unittest.cc @@ -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()); diff --git a/src/lib/stats/observation.cc b/src/lib/stats/observation.cc index 61155bae12..950ed74c6c 100644 --- a/src/lib/stats/observation.cc +++ b/src/lib/stats/observation.cc @@ -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; } diff --git a/src/lib/stats/observation.h b/src/lib/stats/observation.h index 586d3a0fc6..086898fd54 100644 --- a/src/lib/stats/observation.h +++ b/src/lib/stats/observation.h @@ -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 diff --git a/src/lib/stats/stats_mgr.cc b/src/lib/stats/stats_mgr.cc index feeee1287b..910f4a2f8f 100644 --- a/src/lib/stats/stats_mgr.cc +++ b/src/lib/stats/stats_mgr.cc @@ -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()); } diff --git a/src/lib/stats/stats_mgr.h b/src/lib/stats/stats_mgr.h index 554e309e91..86f8d2abb9 100644 --- a/src/lib/stats/stats_mgr.h +++ b/src/lib/stats/stats_mgr.h @@ -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()); diff --git a/src/lib/stats/tests/observation_unittest.cc b/src/lib/stats/tests/observation_unittest.cc index 23486cf4ae..d9e33b2719 100644 --- a/src/lib/stats/tests/observation_unittest.cc +++ b/src/lib/stats/tests/observation_unittest.cc @@ -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. diff --git a/src/lib/stats/tests/stats_mgr_unittest.cc b/src/lib/stats/tests/stats_mgr_unittest.cc index 782cb7c509..29f9cc9e57 100644 --- a/src/lib/stats/tests/stats_mgr_unittest.cc +++ b/src/lib/stats/tests/stats_mgr_unittest.cc @@ -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()); } }; |