]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#3253] Initial commit of MontioredDurationStore
authorThomas Markwalder <tmark@isc.org>
Fri, 23 Feb 2024 21:00:31 +0000 (16:00 -0500)
committerThomas Markwalder <tmark@isc.org>
Mon, 18 Mar 2024 19:12:14 +0000 (19:12 +0000)
Initial implemention of MonitoredDurationStore and UTs

src/hooks/dhcp/perfmon/tests/monitored_duration_store_unittests.cc
src/hooks/dhcp/perfmon/monitored_duration_store.cc
src/hooks/dhcp/perfmon/monitored_duration_store.h
    new files

src/hooks/dhcp/perfmon/Makefile.am
    added monitored_duration_store.cc monitored_duration_store.h

src/hooks/dhcp/perfmon/monitored_duration.*
    Added  ==, !=, < operators

src/hooks/dhcp/perfmon/tests/Makefile.am
    added monitored_duration_store_unittests.cc

src/hooks/dhcp/perfmon/tests/monitored_duration_unittests.cc
    TEST(DurationKey, equalityOperators) - new test

src/hooks/dhcp/perfmon/Makefile.am
src/hooks/dhcp/perfmon/monitored_duration.cc
src/hooks/dhcp/perfmon/monitored_duration.h
src/hooks/dhcp/perfmon/monitored_duration_store.cc [new file with mode: 0644]
src/hooks/dhcp/perfmon/monitored_duration_store.h [new file with mode: 0644]
src/hooks/dhcp/perfmon/tests/Makefile.am
src/hooks/dhcp/perfmon/tests/monitored_duration_store_unittests.cc [new file with mode: 0644]
src/hooks/dhcp/perfmon/tests/monitored_duration_unittests.cc

index 3d96fff9ce9d7f3867b0fce80f9e33004510cd53..f981893c695871a6ef4f238fa47e9dc28a0cea14 100644 (file)
@@ -19,6 +19,7 @@ libperfmon_la_SOURCES += perfmon_log.cc perfmon_log.h
 libperfmon_la_SOURCES += perfmon_messages.cc perfmon_messages.h
 libperfmon_la_SOURCES += monitored_duration.cc monitored_duration.h
 libperfmon_la_SOURCES += alarm.cc alarm.h
+libperfmon_la_SOURCES += monitored_duration_store.cc monitored_duration_store.h
 libperfmon_la_SOURCES += version.cc
 
 libperfmon_la_CXXFLAGS = $(AM_CXXFLAGS)
index d6ddd172fb5a37e47bfec6a2903226e12b724fe9..0da185337769a81e1447be64e8f3e22fca669f48 100644 (file)
@@ -164,6 +164,36 @@ DurationKey::getLabel() const {
     return (oss.str());
 };
 
+bool
+DurationKey::operator==(const DurationKey& other) const {
+    return (
+        (family_ == other.family_) &&
+        (query_type_ == other.query_type_) &&
+        (response_type_ == other.response_type_) &&
+        (start_event_label_ == other.start_event_label_) &&
+        (end_event_label_ == other.end_event_label_) &&
+        (subnet_id_ == other.subnet_id_)
+    );
+}
+
+bool
+DurationKey::operator!=(const DurationKey& other) const {
+    return (!(*this == other));
+}
+
+bool
+DurationKey::operator<(const DurationKey& other) const {
+    return (
+        (family_ < other.family_) ||
+        (query_type_ < other.query_type_) ||
+        (response_type_ < other.response_type_) ||
+        (start_event_label_ < other.start_event_label_) ||
+        (end_event_label_ < other.end_event_label_) ||
+        (subnet_id_ < other.subnet_id_)
+    );
+}
+
+
 // MonitoredDuration methods
 
 MonitoredDuration::MonitoredDuration(uint16_t family,
index 535a7882fedfcc3b88f301ad5c3d1481d4e11655..7dc91b78310df3b869abb32c9a8bb2c800a101ba 100644 (file)
@@ -210,6 +210,27 @@ public:
     /// @throw BadValue is the pairing does not make sense.
     static void validateMessagePair(uint16_t family, uint8_t query_type, uint8_t response_type);
 
+    /// @brief Equality operator.
+    ///
+    /// equality operator to compare two DurationKey objects.
+    /// @param other DurationKey to be compared against.
+    /// @return True the keys are equal
+    bool operator==(const DurationKey& other) const;
+
+    /// @brief Inequality operator.
+    ///
+    /// Inequality operator to compare two DurationKey objects.
+    /// @param other DurationKey to be compared against.
+    /// @return True the keys are not equal
+    bool operator!=(const DurationKey& other) const;
+
+    /// @brief Less than operator.
+    ///
+    /// less than operator to compare two DurationKey objects.
+    /// @param other DurationKey to be compared against.
+    /// @return True other is less than this key
+    bool operator<(const DurationKey& other) const;
+
 protected:
     /// @brief Protocol family AF_INET or AF_INET6.
     uint16_t family_;
diff --git a/src/hooks/dhcp/perfmon/monitored_duration_store.cc b/src/hooks/dhcp/perfmon/monitored_duration_store.cc
new file mode 100644 (file)
index 0000000..7ee473d
--- /dev/null
@@ -0,0 +1,144 @@
+// Copyright (C) 2024 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <exceptions/exceptions.h>
+#include <monitored_duration_store.h>
+#include <util/multi_threading_mgr.h>
+
+using namespace isc;
+using namespace isc::util;
+
+namespace isc {
+namespace perfmon {
+
+MonitoredDurationStore::MonitoredDurationStore(uint16_t family, const Duration& interval_duration)
+    : family_(family), interval_duration_(interval_duration), durations_(), mutex_(new std::mutex) {
+    if (interval_duration_ <= DurationDataInterval::ZERO_DURATION()) {
+        isc_throw(BadValue, "MonitoredDurationStore - invalid interval_duration "
+                            << interval_duration_ << ", must be greater than zero");
+    }
+}
+
+MonitoredDurationPtr
+MonitoredDurationStore::addDuration(DurationKeyPtr key,
+                                    const Duration& sample /* = ZERO_DURATION()*/) {
+    if (!key) {
+        isc_throw(BadValue, "MonitoredDurationStore::addDuration - key is empty");
+    }
+
+    if (key->getFamily() != family_) {
+        isc_throw(BadValue, "MonitoredDurationStore::addDuration - cannot add "
+                            << (family_ == AF_INET ? "v6 key to v4" : "v4 key to v6")
+                            << " store");
+    }
+
+    // Create the duration instance.
+    MonitoredDurationPtr mond;
+    try {
+        mond.reset(new MonitoredDuration(*key, interval_duration_));
+        // Add the first sample if provided.
+        if (sample > DurationDataInterval::ZERO_DURATION()) {
+            mond->addSample(sample);
+        }
+    } catch (const std::exception& ex) {
+        isc_throw(BadValue, "MonitoredDurationStore::addDuration failed: " << ex.what());
+    }
+
+    // Now lock and insert the new duration.
+    {
+        MultiThreadingLock lock(*mutex_);
+        auto ret = durations_.insert(mond);
+        if (ret.second == false) {
+            isc_throw(DuplicateDurationKey,
+                      "MonitoredDurationStore::addDuration: duration already exists for: "
+                      << key->getLabel());
+        }
+    }
+
+    // Return a copy of what we inserted.
+    return (MonitoredDurationPtr(new MonitoredDuration(*mond)));
+}
+
+MonitoredDurationPtr
+MonitoredDurationStore::getDuration(DurationKeyPtr key) {
+    if (!key) {
+        isc_throw(BadValue, "MonitoredDurationStore::getDuration - key is empty");
+    }
+
+    MultiThreadingLock lock(*mutex_);
+#if 0
+    const auto& index = durations_.get<KeyTag>();
+    auto duration_iter = index.find(boost::make_tuple(key->getQueryType(),
+                                                      key->getResponseType(),
+                                                      key->getStartEventLabel(),
+                                                      key->getEndEventLabel(),
+                                                      key->getSubnetId()));
+#else
+    const auto& index = durations_.get<DurationKeyTag>();
+    auto duration_iter = index.find(*key);
+#endif
+    return (duration_iter == index.end() ? MonitoredDurationPtr()
+            : MonitoredDurationPtr(new MonitoredDuration(**duration_iter)));
+}
+
+void
+MonitoredDurationStore::updateDuration(MonitoredDurationPtr& duration) {
+    if (!duration) {
+        isc_throw(BadValue, "MonitoredDurationStore::updateDuration - duration is empty");
+    }
+
+    MultiThreadingLock lock(*mutex_);
+    auto& index = durations_.get<DurationKeyTag>();
+    auto duration_iter = index.find(*duration);
+    if (duration_iter == index.end()) {
+        isc_throw(InvalidOperation, "MonitoredDurationStore::updateDuration duration not found: "
+                  << duration->getLabel());
+    }
+
+    // Use replace() to re-index durations.
+    index.replace(duration_iter, MonitoredDurationPtr(new MonitoredDuration(*duration)));
+}
+
+void
+MonitoredDurationStore::deleteDuration(DurationKeyPtr key) {
+    if (!key) {
+        isc_throw(BadValue, "MonitoredDurationStore::deleteDuration - key is empty");
+    }
+
+    MultiThreadingLock lock(*mutex_);
+    auto& index = durations_.get<DurationKeyTag>();
+    auto duration_iter = index.find(*key);
+    if (duration_iter == index.end()) {
+        // Not there, just return.
+        return;
+    }
+
+    // Remove the context from the store.
+    durations_.erase(duration_iter);
+}
+
+MonitoredDurationCollectionPtr
+MonitoredDurationStore::getAll() {
+    MultiThreadingLock lock(*mutex_);
+    const auto& index = durations_.get<DurationKeyTag>();
+    MonitoredDurationCollectionPtr collection(new MonitoredDurationCollection());
+    for (auto duration_iter = index.begin(); duration_iter != index.end(); ++duration_iter) {
+        collection->push_back(MonitoredDurationPtr(new MonitoredDuration(**duration_iter)));
+    }
+
+    return (collection);
+}
+
+void
+MonitoredDurationStore::clear() {
+    isc_throw(NotImplemented, __FUNCTION__);
+}
+
+} // end of namespace perfmon
+} // end of namespace isc
+
diff --git a/src/hooks/dhcp/perfmon/monitored_duration_store.h b/src/hooks/dhcp/perfmon/monitored_duration_store.h
new file mode 100644 (file)
index 0000000..1f65554
--- /dev/null
@@ -0,0 +1,181 @@
+// Copyright (C) 2024 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef MONITORED_DURATION_STORE_H
+#define MONITORED_DURATION_STORE_H
+
+#include <exceptions/exceptions.h>
+#include <monitored_duration.h>
+
+#include <boost/multi_index/indexed_by.hpp>
+#include <boost/multi_index/member.hpp>
+#include <boost/multi_index/mem_fun.hpp>
+#include <boost/multi_index/ordered_index.hpp>
+#include <boost/multi_index_container.hpp>
+#include <boost/multi_index/composite_key.hpp>
+#include <boost/multi_index/sequenced_index.hpp>
+#include <boost/multi_index/identity.hpp>
+#include <boost/scoped_ptr.hpp>
+
+#include <string>
+
+namespace isc {
+namespace perfmon {
+
+/// @brief Exception thrown when an attempt was made to add a duplicate key
+/// to either the duration or alarm stores.
+class DuplicateDurationKey : public Exception {
+public:
+    DuplicateDurationKey(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+/// @brief Tag for index by primary key (DurationKey).
+struct DurationKeyTag { };
+
+/// @brief A multi index container holding pointers to MonitoredDurations.
+///
+/// The durations in the container may be accessed using different indexes:
+/// - using the full key index
+///   <TBD>
+///
+/// Indexes can be accessed using the index number (from 0 to n) or a
+/// name tag. It is recommended to use the tags to access indexes as
+/// they do not depend on the order of indexes in the container.
+typedef boost::multi_index_container<
+    // It holds pointers to Lease6 objects.
+    MonitoredDurationPtr,
+    boost::multi_index::indexed_by<
+        // Specification of the first index starts here.
+        // This index sorts using DurationKey::operators
+        boost::multi_index::ordered_unique<
+            boost::multi_index::tag<DurationKeyTag>,
+            boost::multi_index::identity<DurationKey>
+        >
+        /// @todo more indexes start here...
+#if 0
+        /// @todo toss this ... did this before I figured out how to use
+        /// DurationKey::operator<
+        ,
+        boost::multi_index::ordered_unique<
+            boost::multi_index::tag<KeyTag>,
+            boost::multi_index::composite_key<
+                MonitoredDuration,
+                // The query packet type
+                boost::multi_index::const_mem_fun<DurationKey, uint8_t,
+                                                  &DurationKey::getQueryType>,
+                // The response packet type
+                boost::multi_index::const_mem_fun<DurationKey, uint8_t,
+                                                  &DurationKey::getResponseType>,
+                // The start event label
+                boost::multi_index::const_mem_fun<DurationKey, std::string,
+                                                  &DurationKey::getStartEventLabel>,
+                // The end event label
+                boost::multi_index::const_mem_fun<DurationKey, std::string,
+                                                  &DurationKey::getEndEventLabel>,
+                // The subnet id
+                boost::multi_index::const_mem_fun<DurationKey, dhcp::SubnetID,
+                                                  &DurationKey::getSubnetId>
+            >
+        >
+#endif
+    >
+> MonitoredDurationContainer;
+
+/// @brief Type for a collection of MonitoredDurationPtrs.
+typedef std::vector<MonitoredDurationPtr> MonitoredDurationCollection;
+
+/// @brief Type for a pointer to a collection of MonitoredDurationPtrs.
+typedef boost::shared_ptr<MonitoredDurationCollection> MonitoredDurationCollectionPtr;
+
+/// @brief Maintains an in-memory store of MonitoredDurations
+///
+/// Provides essential CRUD functions for managing a collection of
+/// MonitoredDurations.  Additionally there are finders that can return
+/// durations by DurationKey  <TBD>
+/// All finders return copies of the durations found, rather than the
+/// stored duration itself.
+class MonitoredDurationStore {
+public:
+    /// @brief Constructor
+    ///
+    /// @param family protocol family AF_INET or AF_INET6
+    /// @param interval_duration the interval duration
+    explicit MonitoredDurationStore(uint16_t family, const Duration& interval_duration);
+
+    /// @brief Destructor
+    ~MonitoredDurationStore() = default;
+
+    /// @brief Creates a new MonitoredDuration and adds it to the store
+    ///
+    /// @param key key value of the duration to create.
+    /// @param sample An initial sample to add to the duration if not zero.
+    ///
+    /// @return pointer to the newly created duration.
+    /// @throw DuplicateDuration if a duration for the given key already exists in
+    /// the store.
+    MonitoredDurationPtr addDuration(DurationKeyPtr key, const Duration& sample
+                                                         = DurationDataInterval::ZERO_DURATION());
+
+    /// @brief Fetches a duration from the store for a given key.
+    ///
+    /// @param key key value of the duration to fetch.
+    ///
+    /// @return Pointer the desired duration or an empty pointer.
+    MonitoredDurationPtr getDuration(DurationKeyPtr key);
+
+    /// @brief Updates a duration in the store.
+    ///
+    /// The duration is assumed to already exist in the store.
+    ///
+    /// @param duration duration to update.
+    ///
+    /// @throw InvalidOperation if MonitoredDuration does not exist in the store.
+    void updateDuration(MonitoredDurationPtr& duration);
+
+    /// @brief Removes the duration from the store.
+    ///
+    /// If the duration does not exist in the store, it simply returns.
+    ///
+    /// @param key key value of the duration to delete.
+    void deleteDuration(DurationKeyPtr key);
+
+    /// @brief Fetches all of the durations (in order by target)
+    ///
+    /// @return a collection of all durations in the store.
+    MonitoredDurationCollectionPtr getAll();
+
+    /// @brief Removes all durations from the store.
+    void clear();
+
+    /// @brief Get protocol family
+    ///
+    /// @return uint16_t containing the family (AF_INET or AF_INET6)
+    uint16_t getFamily() {
+        return (family_);
+    }
+
+private:
+    /// @brief Protocol family AF_INET or AF_INET6.
+    uint16_t family_;
+
+    /// @brief The length of time over data for a single MonitoredDuration is
+    /// accumulated before reporting.
+    Duration interval_duration_;
+
+    /// @brief Container instance.
+    MonitoredDurationContainer durations_;
+
+    /// @brief The mutex used to protect internal state.
+    const boost::scoped_ptr<std::mutex> mutex_;
+};
+
+typedef boost::shared_ptr<MonitoredDurationStore> MonitoredDurationStorePtr;
+
+} // end of namespace ping_check
+} // end of namespace isc
+
+#endif
index b091c961c49a713d72eac23d78f1304a2fc98d65..75dc950b671926f4cd26888c02c2b4ea1e3fc272 100644 (file)
@@ -29,6 +29,7 @@ TESTS += perfmon_unittests
 perfmon_unittests_SOURCES = run_unittests.cc
 perfmon_unittests_SOURCES += monitored_duration_unittests.cc
 perfmon_unittests_SOURCES += alarm_unittests.cc
+perfmon_unittests_SOURCES += monitored_duration_store_unittests.cc
 
 perfmon_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
 
diff --git a/src/hooks/dhcp/perfmon/tests/monitored_duration_store_unittests.cc b/src/hooks/dhcp/perfmon/tests/monitored_duration_store_unittests.cc
new file mode 100644 (file)
index 0000000..d18b3b9
--- /dev/null
@@ -0,0 +1,347 @@
+// Copyright (C) 2024 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/// @file This file contains tests which exercise the MonitoredDurationStore class.
+#include <config.h>
+#include <monitored_duration_store.h>
+#include <dhcp/dhcp6.h>
+#include <testutils/gtest_utils.h>
+#include <testutils/multi_threading_utils.h>
+
+#include <gtest/gtest.h>
+#include <sstream>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::perfmon;
+using namespace isc::test;
+using namespace boost::posix_time;
+
+namespace {
+
+// Verifies MonitoredDurationStore valid construction.
+TEST(MonitoredDurationStore, validConstructors) {
+    MonitoredDurationStorePtr store;
+
+    EXPECT_NO_THROW_LOG(store.reset(new MonitoredDurationStore(AF_INET, milliseconds(5))));
+    ASSERT_TRUE(store);
+
+    MonitoredDurationCollectionPtr durations;
+    ASSERT_NO_THROW_LOG(durations = store->getAll());
+    ASSERT_TRUE(durations);
+    EXPECT_TRUE(durations->empty());
+}
+
+// Verifies MonitoredDurationStore invalid construction.
+TEST(MonitoredDurationStore, invalidConstructors) {
+    MonitoredDurationStorePtr store;
+
+    EXPECT_THROW_MSG(MonitoredDurationStore(AF_INET, milliseconds(0)),
+                     BadValue,
+                     "MonitoredDurationStore - invalid interval_duration"
+                     " 00:00:00, must be greater than zero");
+
+    EXPECT_THROW_MSG(MonitoredDurationStore(AF_INET, milliseconds(-5)),
+                     BadValue,
+                     "MonitoredDurationStore - invalid interval_duration"
+                     " -00:00:00.005000, must be greater than zero");
+}
+
+/// @brief Text fixture class for @c MonitoredDurationStore
+///
+/// In order to facilitate single and multi threaded testing,
+/// individual tests are implemented as methods that are called
+/// from within TEST_F bodies rather than in TEST_F bodies.
+class MonitoredDurationStoreTest : public ::testing::Test {
+public:
+
+    /// @brief Constructor
+    MonitoredDurationStoreTest() {
+    }
+
+    /// @brief Destructor
+    virtual ~MonitoredDurationStoreTest() = default;
+
+    /// @brief Verifies that durations be added to the store and fetched
+    /// by DurationKey.
+    void addDurationTest() {
+        Duration interval_duration(milliseconds(10));
+        MonitoredDurationStore store(AF_INET6, interval_duration);
+
+        // Create a key.
+        DurationKeyPtr key;
+        ASSERT_NO_THROW_LOG(key.reset(new DurationKey(AF_INET6, DHCPV6_SOLICIT, DHCPV6_ADVERTISE,
+                                                       "mt_queued", "process_started", 77)));
+        // Add a duration using the key.
+        MonitoredDurationPtr mond;
+        ASSERT_NO_THROW_LOG(mond = store.addDuration(key, interval_duration));
+        ASSERT_TRUE(mond);
+
+        // Verify we can fetch duration by key.
+        MonitoredDurationPtr found;
+        ASSERT_NO_THROW_LOG(found = store.getDuration(key));
+        ASSERT_TRUE(found);
+
+        // Verify we have different objects but equal keys. Ensures we retrieved
+        // a copy, not what is stored.
+        EXPECT_NE(found, mond);
+        EXPECT_EQ(*found, *mond);
+
+        // Create a second key and duration.
+        DurationKeyPtr key2;
+        ASSERT_NO_THROW_LOG(key2.reset(new DurationKey(AF_INET6, DHCPV6_SOLICIT, DHCPV6_REPLY,
+                                                       "mt_queued", "process_started", 77)));
+        MonitoredDurationPtr mond2;
+        ASSERT_NO_THROW_LOG(mond2 = store.addDuration(key2, interval_duration));
+        ASSERT_TRUE(mond2);
+
+        // Verify we can fetch duration by key.
+        MonitoredDurationPtr found2;
+        ASSERT_NO_THROW_LOG(found2 = store.getDuration(key2));
+        ASSERT_TRUE(found2);
+
+        // Fetch all durations, should return them both.
+        MonitoredDurationCollectionPtr durations;
+        ASSERT_NO_THROW_LOG(durations = store.getAll());
+        ASSERT_TRUE(durations);
+        ASSERT_EQ(durations->size(), 2);
+
+        // They should be in order by key.
+        EXPECT_EQ(*(*durations)[0], *mond);
+        EXPECT_EQ(*(*durations)[1], *mond2);
+    }
+
+    /// @brief Verifies that duplicate durations cannot be added to the store.
+    void addDurationDuplicateTest() {
+        Duration interval_duration(milliseconds(10));
+        MonitoredDurationStore store(AF_INET6, interval_duration);
+
+        // Create a key.
+        DurationKeyPtr key;
+        ASSERT_NO_THROW_LOG(key.reset(new DurationKey(AF_INET6, DHCPV6_SOLICIT, DHCPV6_ADVERTISE,
+                                                       "mt_queued", "process_started", 77)));
+        // Add a duration using the key.
+        MonitoredDurationPtr mond;
+        ASSERT_NO_THROW_LOG(mond = store.addDuration(key, interval_duration));
+        ASSERT_TRUE(mond);
+
+        // Attempting to add it again should evoke a duplicate key exception.
+        ASSERT_THROW_MSG(store.addDuration(key, interval_duration),
+                         DuplicateDurationKey,
+                         "MonitoredDurationStore::addDuration: duration already exists for:"
+                         " SOLICIT-ADVERTISE.mt_queued-process_started.77");
+    }
+
+    /// @brief Verifies that duration and store families must match on add.
+    void addDurationInvalidTest() {
+        Duration interval_duration(milliseconds(10));
+        MonitoredDurationStorePtr store(new MonitoredDurationStore(AF_INET, interval_duration));
+
+        // Create a key.
+        DurationKeyPtr key;
+        ASSERT_NO_THROW_LOG(key.reset(new DurationKey(AF_INET6, DHCPV6_SOLICIT, DHCPV6_ADVERTISE,
+                                                       "mt_queued", "process_started", 77)));
+
+        // Attempting to add it the key should fail.
+        ASSERT_THROW_MSG(store->addDuration(key, interval_duration),
+                         BadValue,
+                         "MonitoredDurationStore::addDuration - cannot add v6 key to v4 store");
+
+        store.reset(new MonitoredDurationStore(AF_INET6, interval_duration));
+        ASSERT_NO_THROW_LOG(key.reset(new DurationKey(AF_INET, DHCPDISCOVER, DHCPOFFER,
+                                                       "mt_queued", "process_started", 77)));
+        // Attempting to add it the key should fail.
+        ASSERT_THROW_MSG(store->addDuration(key, interval_duration),
+                         BadValue,
+                         "MonitoredDurationStore::addDuration - cannot add v4 key to v6 store");
+    }
+
+    /// @brief Verify that durations can be deleted from the store.
+    void deleteDurationTest() {
+        MonitoredDurationStore store(AF_INET, milliseconds(5));
+
+        std::vector<DurationKeyPtr> keys;
+        for (int subnet = 0; subnet < 3; ++subnet) {
+            MonitoredDurationPtr mond;
+            DurationKeyPtr key(new DurationKey(AF_INET, DHCPDISCOVER, DHCPOFFER,
+                                               "socket_received", "buffer_read", subnet));
+            ASSERT_NO_THROW_LOG(mond = store.addDuration(key));
+            ASSERT_TRUE(mond);
+            keys.push_back(key);
+        }
+
+        // Verify we added three of them.
+        auto durations = store.getAll();
+        ASSERT_EQ(durations->size(), 3);
+
+        // Fetch the second duration.
+        MonitoredDurationPtr mond;
+        ASSERT_NO_THROW_LOG(mond = store.getDuration(keys[1]));
+        ASSERT_TRUE(mond);
+        EXPECT_EQ(*mond, *(keys[1]));
+
+        // Delete it.
+        ASSERT_NO_THROW_LOG(store.deleteDuration(mond));
+
+        // Try to fetch it, shouldn't find it.
+        MonitoredDurationPtr mond2;
+        ASSERT_NO_THROW_LOG(mond2 = store.getDuration(mond));
+        ASSERT_FALSE(mond2);
+
+        // Deleting it again should do no harm.
+        ASSERT_NO_THROW_LOG(store.deleteDuration(mond));
+
+        // Verify there are two left.
+        durations = store.getAll();
+        ASSERT_EQ(durations->size(), 2);
+    }
+
+    /// @brief Verify that durations in the store can be updated.
+    void updateDurationTest() {
+        Duration interval_duration(seconds(60));
+        MonitoredDurationStore store(AF_INET6, interval_duration);
+        MonitoredDurationPtr mond;
+
+        // Passing in an empty duration should throw.
+        ASSERT_THROW_MSG(store.updateDuration(mond), BadValue,
+                         "MonitoredDurationStore::updateDuration - duration is empty");
+
+        // Create a key and then a duration.
+        DurationKeyPtr key;
+        ASSERT_NO_THROW_LOG(key.reset(new DurationKey(AF_INET6, DHCPV6_SOLICIT, DHCPV6_ADVERTISE,
+                                                       "mt_queued", "process_started", 77)));
+        ASSERT_NO_THROW(mond.reset(new MonitoredDuration(*key, interval_duration)));
+
+        ASSERT_THROW_MSG(store.updateDuration(mond), InvalidOperation,
+                         "MonitoredDurationStore::updateDuration duration not found:"
+                         " SOLICIT-ADVERTISE.mt_queued-process_started.77");
+
+        // Now add the duration to the store.
+        ASSERT_NO_THROW(store.addDuration(mond));
+
+        // Fetch it.
+        MonitoredDurationPtr found;
+        ASSERT_NO_THROW_LOG(found = store.getDuration(key));
+        ASSERT_TRUE(found);
+
+        // Verify the fetched object is a copy.
+        ASSERT_NE(found, mond);
+        ASSERT_EQ(*found, *mond);
+
+        // Verify that it has no intervals.
+        ASSERT_FALSE(found->getPreviousInterval());
+        ASSERT_FALSE(found->getCurrentInterval());
+
+        // Now add a sample to the duration and update it.
+        mond->addSample(milliseconds(75));
+        ASSERT_NO_THROW(store.updateDuration(mond));
+
+        // Fetch it again and verify there is now a current interval.
+        ASSERT_NO_THROW_LOG(found = store.getDuration(key));
+        ASSERT_FALSE(found->getPreviousInterval());
+
+        DurationDataIntervalPtr current;
+        ASSERT_TRUE(current = found->getCurrentInterval());
+        EXPECT_EQ(current->getOccurrences(), 1);
+        EXPECT_EQ(current->getTotalDuration(), milliseconds(75));
+    }
+
+YOU ARE HERE
+#if 0
+    void getAllAndClearTest() {
+        MonitoredDurationStore store;
+
+        // Add contexts to store.
+        for (int i = 0; i < leases_.size(); ++i) {
+            MonitoredDurationPtr context;
+            ASSERT_NO_THROW_LOG(context = store.addDuration(leases_[i], queries_[i], 1, 100));
+            ASSERT_TRUE(context);
+            EXPECT_EQ(leases_[i], context->getLease());
+            EXPECT_EQ(queries_[i], context->getQuery());
+        }
+
+        // Fetch them all.
+        MonitoredDurationCollectionPtr contexts;
+        ASSERT_NO_THROW_LOG(contexts = store.getAll());
+        ASSERT_EQ(leases_.size(), contexts->size());
+
+        // Verify we got them all in order.
+        int i = 0;
+        for (const auto& context : *contexts) {
+            EXPECT_EQ(leases_[i], context->getLease());
+            EXPECT_EQ(queries_[i], context->getQuery());
+            ++i;
+        }
+
+        // Now clear the store. Verify it's empty.
+        ASSERT_NO_THROW_LOG(store.clear());
+        ASSERT_NO_THROW_LOG(contexts = store.getAll());
+        ASSERT_EQ(0, contexts->size());
+
+        // Verify clearing an empty store does no harm.
+        ASSERT_NO_THROW_LOG(store.clear());
+    }
+#endif
+};
+
+TEST_F(MonitoredDurationStoreTest, addDuration) {
+    addDurationTest();
+}
+
+TEST_F(MonitoredDurationStoreTest, addDurationMultiThreading) {
+    MultiThreadingTest mt;
+    addDurationTest();
+}
+
+TEST_F(MonitoredDurationStoreTest, addDurationDuplicate) {
+    addDurationDuplicateTest();
+}
+
+TEST_F(MonitoredDurationStoreTest, addDurationDuplicateMultiThreading) {
+    MultiThreadingTest mt;
+    addDurationDuplicateTest();
+}
+
+TEST_F(MonitoredDurationStoreTest, addDurationInvalid) {
+    addDurationInvalidTest();
+}
+
+TEST_F(MonitoredDurationStoreTest, addDurationInvalidMultiThreading) {
+    MultiThreadingTest mt;
+    addDurationInvalidTest();
+}
+
+TEST_F(MonitoredDurationStoreTest, deleteDuration) {
+    deleteDurationTest();
+}
+
+TEST_F(MonitoredDurationStoreTest, deleteDurationMultiThreading) {
+    MultiThreadingTest mt;
+    deleteDurationTest();
+}
+
+TEST_F(MonitoredDurationStoreTest, updateDuration) {
+    updateDurationTest();
+}
+
+TEST_F(MonitoredDurationStoreTest, updateDurationMultiThreading) {
+    MultiThreadingTest mt;
+    updateDurationTest();
+}
+
+#if 0
+TEST_F(MonitoredDurationStoreTest, getAllAndClear) {
+    getAllAndClearTest();
+}
+
+TEST_F(MonitoredDurationStoreTest, getAllAndClearMultiThreading) {
+    MultiThreadingTest mt;
+    getAllAndClearTest();
+}
+#endif
+
+} // end of anonymous namespace
index 7b295d3d87853f679faf74426d82a4b1d5408051..e9223472acbca4f1db95d98c5089e06499097940 100644 (file)
@@ -232,6 +232,23 @@ TEST(DurationKey, validateMessagePairs6) {
     }
 }
 
+/// @brief Verify DurationKey equality operator
+TEST(DurationKey, equalityOperators) {
+    DurationKeyPtr refkey;
+    DurationKeyPtr compkey;
+
+    ASSERT_NO_THROW_LOG(refkey.reset(new DurationKey(AF_INET6, DHCPV6_REQUEST, DHCPV6_REPLY,
+                                     "event_2", "event_3", 100)));
+
+    ASSERT_NO_THROW_LOG(compkey.reset(new DurationKey(AF_INET6, DHCPV6_REQUEST, DHCPV6_REPLY,
+                                      "event_2", "event_3", 100)));
+    EXPECT_EQ(*compkey, *refkey);
+
+    ASSERT_NO_THROW_LOG(compkey.reset(new DurationKey(AF_INET6, DHCPV6_SOLICIT, DHCPV6_REPLY,
+                                      "event_2", "event_3", 100)));
+    EXPECT_NE(*compkey, *refkey);
+}
+
 // Verifies MonitoredDuration valid construction.
 TEST(MonitoredDuration, validConstructors) {
     MonitoredDurationPtr mond;