]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#3328] New Unit tests
authorThomas Markwalder <tmark@isc.org>
Thu, 13 Jun 2024 18:56:52 +0000 (14:56 -0400)
committerFrancis Dupont <fdupont@isc.org>
Sat, 15 Jun 2024 07:17:39 +0000 (09:17 +0200)
/src/hooks/dhcp/perfmon/tests/monitored_duration_unittests.cc
    TEST(DurationKey, toElement)
    TEST(MonitoredDuration, toElement)
    TEST(MonitoredDuration, toValueRow)

/src/hooks/dhcp/perfmon/tests/perfmon_cmds_unittests.cc
    TEST_F(PerfMonCmdTest4, invalidPerfMonGetAllDurations)
    TEST_F(PerfMonCmdTest6, invalidPerfMonGetAllDurations)
    TEST_F(PerfMonCmdTest4, perfMonGetAllDurationsResultSetFalse)
    TEST_F(PerfMonCmdTest4, perfMonGetAllDurationsResultSetTrue)
    TEST_F(PerfMonCmdTest6, perfMonGetAllDurationsResultSetFalse)
    TEST_F(PerfMonCmdTest6, perfMonGetAllDurationsResultSetTrue)

src/hooks/dhcp/perfmon/tests/monitored_duration_unittests.cc
src/hooks/dhcp/perfmon/tests/perfmon_cmds_unittests.cc

index 842902ce3a6aee50f804773c16add8fda87a32c2..b64d857c8035e12717f5f6eef5f0e5e51eb417a1 100644 (file)
@@ -6,7 +6,9 @@
 
 #include <config.h>
 #include <monitored_duration.h>
+#include <cc/data.h>
 #include <dhcp/dhcp6.h>
+#include <util/boost_time_utils.h>
 #include <testutils/gtest_utils.h>
 
 #include <gtest/gtest.h>
 #include <unordered_set>
 
 using namespace isc;
+using namespace isc::data;
 using namespace isc::dhcp;
 using namespace isc::perfmon;
+using namespace isc::util;
 using namespace boost::posix_time;
 
 namespace {
@@ -255,6 +259,42 @@ TEST(DurationKey, equalityOperators) {
     EXPECT_NE(*compkey, *refkey);
 }
 
+// Verifies the DurationKey::toElement()
+TEST(DurationKey, toElement) {
+    DurationKeyPtr key;
+
+    // Create valid v4 key, verify contents and label.
+    ASSERT_NO_THROW_LOG(key.reset(new DurationKey(AF_INET, DHCPDISCOVER, DHCPOFFER,
+                                  "process_started", "process_completed",
+                                  SUBNET_ID_GLOBAL)));
+    ASSERT_TRUE(key);
+
+    ElementPtr ref_key_elem = Element::createMap();
+    ref_key_elem->set("query-type", Element::create("DHCPDISCOVER"));
+    ref_key_elem->set("response-type", Element::create("DHCPOFFER"));
+    ref_key_elem->set("start-event", Element::create("process_started"));
+    ref_key_elem->set("stop-event", Element::create("process_completed"));
+    ref_key_elem->set("subnet-id", Element::create(SUBNET_ID_GLOBAL));
+
+    auto key_elem = key->toElement();
+    EXPECT_EQ(*ref_key_elem, *key_elem);
+
+    // Create valid v6 key, verify contents and label.
+    ASSERT_NO_THROW_LOG(key.reset(new DurationKey(AF_INET6, DHCPV6_SOLICIT, DHCPV6_ADVERTISE,
+                                  "mt_queued", "process_started", 77)));
+    ASSERT_TRUE(key);
+
+    ref_key_elem = Element::createMap();
+    ref_key_elem->set("query-type", Element::create("SOLICIT"));
+    ref_key_elem->set("response-type", Element::create("ADVERTISE"));
+    ref_key_elem->set("start-event", Element::create("mt_queued"));
+    ref_key_elem->set("stop-event", Element::create("process_started"));
+    ref_key_elem->set("subnet-id", Element::create(77));
+
+    key_elem = key->toElement();
+    EXPECT_EQ(*ref_key_elem, *key_elem);
+}
+
 // Verifies MonitoredDuration valid construction.
 TEST(MonitoredDuration, validConstructors) {
     MonitoredDurationPtr mond;
@@ -525,4 +565,128 @@ TEST(MonitoredDuration, expireInterval) {
     EXPECT_EQ(mond->getCurrentIntervalStart(), PktEvent::MIN_TIME());
 }
 
+// Verifies the MonitoredDuration::toElement(). We do not bother with
+// a v6 version of this test as family only influences DurationKey content
+// and that is tested elsewhere.
+TEST(MonitoredDuration, toElement) {
+    MonitoredDurationPtr duration;
+    Duration interval_duration(milliseconds(50));
+
+    // Create valid v4 duration.
+    ASSERT_NO_THROW_LOG(duration.reset(new MonitoredDuration(AF_INET, DHCPDISCOVER, DHCPOFFER,
+                                                             "process_started", "process_completed",
+                                                             SUBNET_ID_GLOBAL, interval_duration)));
+    ASSERT_TRUE(duration);
+    duration->addSample(microseconds(5));
+    duration->addSample(microseconds(2));
+    duration->addSample(microseconds(7));
+
+    ElementPtr ref_duration_elem = Element::createMap();
+    // Add the key.
+    ElementPtr ref_key_elem = Element::createMap();
+    ref_key_elem->set("query-type", Element::create("DHCPDISCOVER"));
+    ref_key_elem->set("response-type", Element::create("DHCPOFFER"));
+    ref_key_elem->set("start-event", Element::create("process_started"));
+    ref_key_elem->set("stop-event", Element::create("process_completed"));
+    ref_key_elem->set("subnet-id", Element::create(SUBNET_ID_GLOBAL));
+    ref_duration_elem->set("duration-key", ref_key_elem);
+
+    // Add the data. Should have empty values as we do not have a previous interval.
+    ref_duration_elem->set("ave-duration-usecs", Element::create(0));
+    ref_duration_elem->set("max-duration-usecs", Element::create(0));
+    ref_duration_elem->set("min-duration-usecs", Element::create(0));
+    ref_duration_elem->set("occurrences", Element::create(0));
+    ref_duration_elem->set("start-time", Element::create("<none>"));
+    ref_duration_elem->set("total-duration-usecs", Element::create(0));
+
+    // Generate the valueRow Element and compare it to the reference.
+    auto duration_elem = duration->toElement();
+    ASSERT_TRUE(duration_elem);
+    EXPECT_EQ(*ref_duration_elem, *duration_elem);
+
+    // Now expire the current interval so we'll have a previous interval.
+    duration->expireCurrentInterval();
+    auto previous_interval = duration->getPreviousInterval();
+    ASSERT_TRUE(previous_interval);
+
+    // Replace the data values with those from the new previous interval.
+    ref_duration_elem->set("ave-duration-usecs", Element::create(4));
+    ref_duration_elem->set("max-duration-usecs", Element::create(7));
+    ref_duration_elem->set("min-duration-usecs", Element::create(2));
+    ref_duration_elem->set("occurrences", Element::create(3));
+    ref_duration_elem->set("start-time",
+                           Element::create(ptimeToText(previous_interval->getStartTime())));
+    ref_duration_elem->set("total-duration-usecs", Element::create(14));
+
+    // Generate the valueRow Element and compare it to the reference.
+    duration_elem = duration->toElement();
+    ASSERT_TRUE(duration_elem);
+    EXPECT_EQ(*ref_duration_elem, *duration_elem);
+}
+
+// Verifies the MonitoredDuration::toElement(). We do not bother with
+// a v4 version of this test as family only influences DurationKey content
+// and that is tested elsewhere.
+TEST(MonitoredDuration, toValueRow) {
+    MonitoredDurationPtr duration;
+    Duration interval_duration(milliseconds(50));
+    auto ten_ms = milliseconds(10);
+
+    // Create valid v4 duration.
+    ASSERT_NO_THROW_LOG(duration.reset(new MonitoredDuration(AF_INET6, DHCPV6_SOLICIT, DHCPV6_ADVERTISE,
+                                                             "process_started", "process_completed",
+                                                             SUBNET_ID_GLOBAL, interval_duration)));
+    ASSERT_TRUE(duration);
+    duration->addSample(microseconds(5));
+    duration->addSample(microseconds(2));
+    duration->addSample(microseconds(7));
+
+    ElementPtr ref_row_elem = Element::createList();
+    // Add key values first.
+    ref_row_elem->add(Element::create("SOLICIT"));              // query-type
+    ref_row_elem->add(Element::create("ADVERTISE"));            // response-type
+    ref_row_elem->add(Element::create("process_started"));      // start-event
+    ref_row_elem->add(Element::create("process_completed"));    // stop-event
+    ref_row_elem->add(Element::create(SUBNET_ID_GLOBAL));       // subnet-id
+
+    // Remember where the data values begin.
+    auto data_start = ref_row_elem->size();
+
+    // Add the data values. Should have empty values as we do not have a previous interval.
+    ref_row_elem->add(Element::create("<none>"));  // start-time
+    ref_row_elem->add(Element::create(0));         // occurrences
+    ref_row_elem->add(Element::create(0));         // min-duration-usecs
+    ref_row_elem->add(Element::create(0));         // max-duration-usecs
+    ref_row_elem->add(Element::create(0));         // total-duration-usecs
+    ref_row_elem->add(Element::create(0));         // ave-duration-usecs
+
+    // Generate the valueRow Element and compare it to the reference.
+    auto row_elem = duration->toValueRow();
+    ASSERT_TRUE(row_elem);
+    EXPECT_EQ(*ref_row_elem, *row_elem);
+
+    // Now expire the current interval so we'll have a previous interval.
+    duration->expireCurrentInterval();
+    auto previous_interval = duration->getPreviousInterval();
+    ASSERT_TRUE(previous_interval);
+
+    // Remove the old reference data values.
+    while (ref_row_elem->size() > data_start) {
+        ref_row_elem->remove(data_start);
+    }
+
+    // Add the new reference data values.
+    ref_row_elem->add(Element::create(ptimeToText(previous_interval->getStartTime())));
+    ref_row_elem->add(Element::create(3));  // occurrences
+    ref_row_elem->add(Element::create(2));  // min-duration-usecs
+    ref_row_elem->add(Element::create(7));  // max-duration-usecs
+    ref_row_elem->add(Element::create(14)); // total-duration-usecs
+    ref_row_elem->add(Element::create(4));  // ave-duration-usecs
+
+    // Generate the valueRow Element and compare it to the reference.
+    row_elem = duration->toValueRow();
+    ASSERT_TRUE(row_elem);
+    EXPECT_EQ(*ref_row_elem, *row_elem);
+}
+
 } // end of anonymous namespace
index 02bce8512fe44408ba25b340793759230daf369f..ee0c538a741203d7d2c3868e4782b3dfa1f624ee 100644 (file)
@@ -16,6 +16,7 @@
 #include <testutils/log_utils.h>
 #include <testutils/gtest_utils.h>
 #include <testutils/multi_threading_utils.h>
+#include <util/boost_time_utils.h>
 
 #include <gtest/gtest.h>
 #include <list>
@@ -31,6 +32,7 @@ using namespace isc::hooks;
 using namespace isc::perfmon;
 using namespace isc::stats;
 using namespace isc::test;
+using namespace isc::util;
 using namespace isc::dhcp::test;
 using namespace boost::posix_time;
 
@@ -91,28 +93,22 @@ public:
         mgr_->configure(json_elements);
     }
 
-    /// @brief Make a valid family-specific query.
+    /// @brief Adds durations to the store.
     ///
-    /// @return if family is AF_INET return a pointer to a DHCPDISCOVER
-    /// otherwise a pointer to a DHCPV6_SOLICIT.
-    PktPtr makeFamilyQuery() {
-        if (family_ == AF_INET) {
-            return (PktPtr(new Pkt4(DHCPDISCOVER, 7788)));
+    /// @param family protocol family to test, AF_INET or AF_INET6.
+    void addDurations(uint16_t family) {
+        // Create two keys where the stop event for the first key is the start
+        // event for the second key.
+        DurationKeyPtr key1(new DurationKey(family, 0, 0, "socket_received", "buffer_read", 1));
+        DurationKeyPtr key2(new DurationKey(family, 0, 0, "buffer_read", "process_started", 1));
+
+        // Make multiple calls to addDurationSample() for each key, starting with key1.
+        auto store = mgr_->getDurationStore();
+        ASSERT_TRUE(store);
+        for (int i = 0; i < 4; ++i) {
+            ASSERT_NO_THROW_LOG(store->addDurationSample(key1, milliseconds(1)));
+            ASSERT_NO_THROW_LOG(store->addDurationSample(key2, milliseconds(2)));
         }
-
-        return (PktPtr(new Pkt6(DHCPV6_SOLICIT, 7788)));
-    }
-
-    /// @brief Make a valid family-specific response.
-    ///
-    /// @return if family is AF_INET return a pointer to a DHCPOFFER
-    /// otherwise a pointer to a DHCPV6_ADVERTISE.
-    PktPtr makeFamilyResponse() {
-        if (family_ == AF_INET) {
-            return (PktPtr(new Pkt4(DHCPOFFER, 7788)));
-        }
-
-        return (PktPtr(new Pkt6(DHCPV6_ADVERTISE, 7788)));
     }
 
     /// @brief Tests specified command and verifies response.
@@ -178,6 +174,8 @@ public:
         // Run the command handler appropriate for the given command name.
         if (command_name == "perfmon-control") {
             static_cast<void>(mgr_->perfmonControlHandler(*callout_handle));
+        } else if (command_name == "perfmon-get-all-durations") {
+            static_cast<void>(mgr_->perfmonGetAllDurationsHandler(*callout_handle));
         } else {
             ADD_FAILURE() << "unrecognized command '" << command_name << "'";
         }
@@ -239,7 +237,7 @@ public:
         }
     }
 
-    // Verify that invalid perfmon-control commands are caught.
+    /// @brief Verify that invalid perfmon-control commands are caught.
     void testInvalidPerfMonControl() {
         struct Scenario {
             int line_;              // Scenario line number
@@ -283,7 +281,7 @@ public:
         }
     }
 
-    // Verify that valid perfmon-control are processed correctly.
+    /// @brief Verify that valid perfmon-control are processed correctly.
     void testValidPerfMonControl() {
         struct Scenario {
             int line_;                      // Scenario line number
@@ -387,6 +385,158 @@ public:
         }
     }
 
+    /// @brief Verify that invalid perfmon-get-all-durations commands are caught.
+    void testInvalidPerfMonGetAllDurations() {
+        struct Scenario {
+            int line_;              // Scenario line number
+            std::string cmd_;       // JSON command text
+            int exp_result_;        // Expected result code
+            std::string exp_text_;  // Expected result text
+        };
+
+        std::list<Scenario> scenarios = {
+            {
+                __LINE__,
+                R"({
+                    "command": "perfmon-get-all-durations",
+                    "arguments": {
+                        "result-set-format": "bogus"
+                    }
+                })",
+                CONTROL_RESULT_ERROR,
+                "'result-set-format' parameter is not a boolean"
+            },
+            {
+                __LINE__,
+                R"({
+                    "command": "perfmon-get-all-durations",
+                    "arguments": {
+                        "bogus": 23
+                    }
+                })",
+                CONTROL_RESULT_ERROR,
+                "spurious 'bogus' parameter"
+            }
+        };
+
+        for (const auto& scenario : scenarios) {
+            stringstream oss;
+            oss << "scenario at line: " << scenario.line_;
+            SCOPED_TRACE(oss.str());
+            ConstElementPtr answer = testCommand(scenario.cmd_,
+                                                 scenario.exp_result_,
+                                                 scenario.exp_text_);
+        }
+    }
+
+    /// @brief Veriies that a valid perfmon-get-all-durations command with
+    /// result-set-format set false, returns all durations correctly.
+    void testPerfMonGetAllDurationsResultSetFalse() {
+        std::string now_str = ptimeToText(PktEvent::now());
+        std::string cmd =
+                R"({
+                    "command": "perfmon-get-all-durations",
+                    "arguments": {
+                        "result-set-format": false
+                    }
+                })";
+
+        addDurations(family_);
+
+        auto ref_durations = mgr_->getDurationStore()->getAll();
+        auto ref_time = PktEvent::now();
+        std::ostringstream oss;
+        oss << "perfmon-get-all-durations: " << ref_durations->size() << " found";
+        ConstElementPtr answer = testCommand(cmd, CONTROL_RESULT_SUCCESS, oss.str());
+
+        checkAnswerAgainstDurations(ref_durations, answer, ref_time, false);
+    }
+
+    /// @brief Veriies that a valid perfmon-get-all-durations with result-set-format
+    /// set true, returns all durations correctly.
+    void testPerfMonGetAllDurationsResultSetTrue() {
+        std::string now_str = ptimeToText(PktEvent::now());
+        std::string cmd =
+                R"({
+                    "command": "perfmon-get-all-durations",
+                    "arguments": {
+                        "result-set-format": true
+                    }
+                })";
+
+        addDurations(family_);
+        auto ref_durations = mgr_->getDurationStore()->getAll();
+        ASSERT_TRUE(ref_durations);
+
+        auto ref_time = PktEvent::now();
+        std::ostringstream oss;
+        oss << "perfmon-get-all-durations: " << ref_durations->size() << " found";
+        ConstElementPtr answer = testCommand(cmd, CONTROL_RESULT_SUCCESS, oss.str());
+
+        checkAnswerAgainstDurations(ref_durations, answer, ref_time, true);
+    }
+
+    /// @brief Verifies that the command response content against a list of
+    /// MonitoredDurations and the expected format.
+    ///
+    /// @param ref_durations list of expected MonitoredDurations in the order they
+    /// should appear in the results.
+    /// @param anwswer complete command answer to check
+    /// @param ref_time timestamp used to compare againt the "timestamp" in the answer.
+    /// @param result_set_format expected format style of the answer
+    void checkAnswerAgainstDurations(const MonitoredDurationCollectionPtr ref_durations,
+                                    ConstElementPtr answer,
+                                    const Timestamp& ref_time,
+                                    bool result_set_format) {
+        // Sanity check.
+        ASSERT_TRUE(ref_durations);
+        auto ref_count = ref_durations->size();
+        ASSERT_TRUE(answer);
+
+        // Verify content as either list elements or result-set.
+        if (!result_set_format) {
+            auto durations_list = answer->find("arguments/durations");
+            ASSERT_TRUE(durations_list);
+            EXPECT_EQ(ref_count, durations_list->size());
+
+            int i = 0;
+            for (const auto& ref_duration : *ref_durations) {
+                auto duration_elem = durations_list->get(i);
+                ASSERT_TRUE(duration_elem);
+                EXPECT_EQ(*(duration_elem), *(ref_duration->toElement()));
+                ++i;
+            }
+        } else {
+            auto elem = answer->find("arguments/result-set-format");
+            ASSERT_TRUE(elem);
+            ASSERT_TRUE(elem->boolValue());
+
+            auto result_set = answer->find("arguments/durations-result-set");
+            ASSERT_TRUE(result_set);
+            auto columns = result_set->find("columns");
+            ASSERT_TRUE(columns);
+            EXPECT_EQ(*columns, *MonitoredDuration::valueRowColumns());
+
+            auto rows = result_set->find("rows");
+            ASSERT_TRUE(rows);
+
+            int i = 0;
+            for (const auto& ref_duration : *ref_durations) {
+                auto row  = rows->get(i);
+                ASSERT_TRUE(row);
+                EXPECT_EQ(*row, *(ref_duration->toValueRow()));
+                ++i;
+            }
+        }
+
+        auto elem = answer->find("arguments/interval-width-secs");
+        ASSERT_TRUE(elem);
+        EXPECT_EQ(mgr_->getIntervalWidthSecs(), elem->intValue());
+
+        elem = answer->find("arguments/timestamp");
+        ASSERT_TRUE(elem);
+        EXPECT_GE(elem->stringValue(), ptimeToText(ref_time));
+    }
 
     /// @brief Protocol family AF_INET or AF_INET6
     uint16_t family_;
@@ -437,5 +587,28 @@ TEST_F(PerfMonCmdTest6, validPerfMonControl) {
     testValidPerfMonControl();
 }
 
+TEST_F(PerfMonCmdTest4, invalidPerfMonGetAllDurations) {
+    testInvalidPerfMonGetAllDurations();
+}
+
+TEST_F(PerfMonCmdTest6, invalidPerfMonGetAllDurations) {
+    testInvalidPerfMonGetAllDurations();
+}
+
+TEST_F(PerfMonCmdTest4, perfMonGetAllDurationsResultSetFalse) {
+    testPerfMonGetAllDurationsResultSetFalse();
+}
+
+TEST_F(PerfMonCmdTest4, perfMonGetAllDurationsResultSetTrue) {
+    testPerfMonGetAllDurationsResultSetTrue();
+}
+
+TEST_F(PerfMonCmdTest6, perfMonGetAllDurationsResultSetFalse) {
+    testPerfMonGetAllDurationsResultSetFalse();
+}
+
+TEST_F(PerfMonCmdTest6, perfMonGetAllDurationsResultSetTrue) {
+    testPerfMonGetAllDurationsResultSetTrue();
+}
 
 } // end of anonymous namespace