]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#2692] Added unit tests
authorFrancis Dupont <fdupont@isc.org>
Tue, 5 Mar 2024 10:13:58 +0000 (11:13 +0100)
committerFrancis Dupont <fdupont@isc.org>
Wed, 20 Mar 2024 20:06:11 +0000 (21:06 +0100)
src/bin/dhcp4/tests/Makefile.am
src/bin/dhcp4/tests/callout_library_4.cc [new file with mode: 0644]
src/bin/dhcp4/tests/hooks_unittest.cc
src/bin/dhcp4/tests/test_libraries.h.in
src/bin/dhcp6/tests/Makefile.am
src/bin/dhcp6/tests/callout_library_4.cc [new file with mode: 0644]
src/bin/dhcp6/tests/hooks_unittest.cc
src/bin/dhcp6/tests/test_libraries.h.in

index b4c4c2db63aeb27ba7c4b2582b0c1d4abfde49dd..1a2c00eb26bfca2c984c901ef33c7af5cd9678ee 100644 (file)
@@ -64,8 +64,13 @@ libco3_la_CXXFLAGS = $(AM_CXXFLAGS)
 libco3_la_CPPFLAGS = $(AM_CPPFLAGS)
 libco3_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
 
+libco4_la_SOURCES  = callout_library_4.cc callout_library_common.h
+libco4_la_CXXFLAGS = $(AM_CXXFLAGS)
+libco4_la_CPPFLAGS = $(AM_CPPFLAGS)
+libco4_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
+
 # Don't install test libraries.
-noinst_LTLIBRARIES = libco1.la libco2.la libco3.la
+noinst_LTLIBRARIES = libco1.la libco2.la libco3.la libco4.la
 
 # C++ tests
 PROGRAM_TESTS = dhcp4_unittests
diff --git a/src/bin/dhcp4/tests/callout_library_4.cc b/src/bin/dhcp4/tests/callout_library_4.cc
new file mode 100644 (file)
index 0000000..ba0c362
--- /dev/null
@@ -0,0 +1,105 @@
+// Copyright (C) 2018-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
+/// @brief Callout library for testing execution of the dhcp4_srv_configured
+/// hook point.
+///
+static const int LIBRARY_NUMBER = 4;
+
+#include <config.h>
+
+#include <asiolink/io_service.h>
+#include <dhcp4/tests/callout_library_common.h>
+#include <dhcpsrv/srv_config.h>
+
+#include <string>
+#include <vector>
+
+using namespace isc::asiolink;
+using namespace isc::hooks;
+
+namespace {
+
+// Start service method: always throw.
+void start_service(void) {
+    isc_throw(isc::Unexpected, "start service failed");
+};
+
+} // end anonymous
+
+// Functions accessed by the hooks framework use C linkage to avoid the name
+// mangling that accompanies use of the C++ compiler as well as to avoid
+// issues related to namespaces.
+extern "C" {
+
+int
+do_load_impl(LibraryHandle& handle) {
+    // Determine if this callout is configured to fail.
+    isc::dhcp::SrvConfigPtr config;
+    isc::data::ConstElementPtr const& parameters(handle.getParameters());
+    isc::data::ConstElementPtr mode_element(parameters ? parameters->get("mode") : 0);
+    std::string mode(mode_element ? mode_element->stringValue() : "");
+    if (mode == "fail-on-load") {
+        return (1);
+    }
+    return (0);
+}
+
+int (*do_load)(LibraryHandle& handle) = do_load_impl;
+
+int (*do_unload)();
+
+/// @brief Callout which appends library number and provided arguments to
+/// the marker file for dhcp4_srv_configured callout.
+///
+/// @param handle callout handle passed to the callout.
+///
+/// @return 0 on success, 1 otherwise.
+int
+dhcp4_srv_configured(CalloutHandle& handle) {
+
+    // Append library number.
+    if (appendDigit(SRV_CONFIG_MARKER_FILE)) {
+        return (1);
+    }
+
+    // Append argument names.
+    std::vector<std::string> args = handle.getArgumentNames();
+    for (auto const& arg : args) {
+        if (appendArgument(SRV_CONFIG_MARKER_FILE, arg.c_str()) != 0) {
+            return (1);
+        }
+    }
+
+    // Get the IO context to post start_service on it.
+    std::string error("");
+    IOServicePtr io_context;
+    try {
+        handle.getArgument("io_context", io_context);
+        if (!io_context) {
+            error = "null io_context";
+        }
+        io_context->post(start_service);
+    } catch (const std::exception& ex) {
+        error = "no io_context in arguments";
+    }
+    if (!error.empty()) {
+        handle.setArgument("error", error);
+        handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
+    }
+
+    return (0);
+}
+
+/// @brief This function is called to retrieve the multi-threading compatibility.
+///
+/// @return 1 which means compatible with multi-threading.
+int multi_threading_compatible() {
+    return (1);
+}
+
+} // end extern "C"
index ce9ba751a3926172df249074746e868ce81b032d..c0b086d467bebb3cf33e9db5642422e064ce92ac 100644 (file)
@@ -4039,4 +4039,65 @@ TEST_F(HooksDhcpv4SrvTest, lease4OfferDiscoverDecline) {
     EXPECT_EQ(expected_address, callback_lease4_->addr_);
 }
 
+// Checks that postponed hook start service can fail.
+TEST_F(LoadUnloadDhcpv4SrvTest, StartServiceFail) {
+    boost::shared_ptr<ControlledDhcpv4Srv> srv(new ControlledDhcpv4Srv(0));
+
+    // Ensure no marker files to start with.
+    ASSERT_FALSE(checkMarkerFileExists(LOAD_MARKER_FILE));
+    ASSERT_FALSE(checkMarkerFileExists(UNLOAD_MARKER_FILE));
+    ASSERT_FALSE(checkMarkerFileExists(SRV_CONFIG_MARKER_FILE));
+
+    // Minimal valid configuration for the server. It includes the
+    // section which loads the callout library #4, which implements
+    // dhcp4_srv_configured callout and a failing start service.
+    string config_str =
+        "{"
+        "    \"interfaces-config\": {"
+        "        \"interfaces\": [ ]"
+        "    },"
+        "    \"rebind-timer\": 2000,"
+        "    \"renew-timer\": 1000,"
+        "    \"subnet4\": [ ],"
+        "    \"valid-lifetime\": 4000,"
+        "    \"lease-database\": {"
+        "         \"type\": \"memfile\","
+        "         \"persist\": false"
+        "    },"
+        "    \"hooks-libraries\": ["
+        "        {"
+        "            \"library\": \"" + std::string(CALLOUT_LIBRARY_4) + "\""
+        "        }"
+        "    ]"
+        "}";
+
+    ConstElementPtr config = Element::fromJSON(config_str);
+
+    // Configure the server.
+    ConstElementPtr answer;
+    ASSERT_NO_THROW(answer = srv->processConfig(config));
+
+    // Make sure there was an error with expected message.
+    int status_code;
+    parseAnswer(status_code, answer);
+    EXPECT_EQ(1, status_code);
+    EXPECT_EQ(answer->str(),
+              R"({ "result": 1, "text": "Error initializing hooks: start service failed" })");
+
+    // The hook library should have been loaded.
+    EXPECT_TRUE(checkMarkerFile(LOAD_MARKER_FILE, "4"));
+    EXPECT_FALSE(checkMarkerFileExists(UNLOAD_MARKER_FILE));
+    // The dhcp4_srv_configured should have been invoked and the provided
+    // parameters should be recorded.
+    EXPECT_TRUE(checkMarkerFile(SRV_CONFIG_MARKER_FILE,
+                                "4io_contextjson_confignetwork_stateserver_config"));
+
+    // Destroy the server, instance which should unload the libraries.
+    srv.reset();
+
+    // The server was destroyed, so the unload() function should now
+    // include the library number in its marker file.
+    EXPECT_TRUE(checkMarkerFile(UNLOAD_MARKER_FILE, "4"));
+}
+
 }  // namespace
index 9b9a243b8bdd2123ba25f1194f6228ea5e83c35d..af659f7e898adb9f507dd05a93701911a84ba7dd 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2013-2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-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
@@ -22,6 +22,7 @@ namespace {
 const char* const CALLOUT_LIBRARY_1 = "@abs_builddir@/.libs/libco1.so";
 const char* const CALLOUT_LIBRARY_2 = "@abs_builddir@/.libs/libco2.so";
 const char* const CALLOUT_LIBRARY_3 = "@abs_builddir@/.libs/libco3.so";
+const char* const CALLOUT_LIBRARY_4 = "@abs_builddir@/.libs/libco4.so";
 
 // Name of a library which is not present.
 const char* const NOT_PRESENT_LIBRARY = "@abs_builddir@/.libs/libnothere.so";
index f7853b32e1aa9aec01997931f2ee96d4dd05bf7e..12f4fc222598d172f0918966003a2c13f491be1f 100644 (file)
@@ -65,8 +65,13 @@ libco3_la_CXXFLAGS = $(AM_CXXFLAGS)
 libco3_la_CPPFLAGS = $(AM_CPPFLAGS)
 libco3_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
 
+libco4_la_SOURCES  = callout_library_4.cc callout_library_common.h
+libco4_la_CXXFLAGS = $(AM_CXXFLAGS)
+libco4_la_CPPFLAGS = $(AM_CPPFLAGS)
+libco4_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
+
 # Don't install test libraries.
-noinst_LTLIBRARIES = libco1.la libco2.la libco3.la
+noinst_LTLIBRARIES = libco1.la libco2.la libco3.la libco4.la
 
 # C++ tests
 PROGRAM_TESTS = dhcp6_unittests
diff --git a/src/bin/dhcp6/tests/callout_library_4.cc b/src/bin/dhcp6/tests/callout_library_4.cc
new file mode 100644 (file)
index 0000000..802aace
--- /dev/null
@@ -0,0 +1,105 @@
+// Copyright (C) 2018-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
+/// @brief Callout library for testing execution of the dhcp6_srv_configured
+/// hook point.
+///
+static const int LIBRARY_NUMBER = 4;
+
+#include <config.h>
+
+#include <asiolink/io_service.h>
+#include <dhcp6/tests/callout_library_common.h>
+#include <dhcpsrv/srv_config.h>
+
+#include <string>
+#include <vector>
+
+using namespace isc::asiolink;
+using namespace isc::hooks;
+
+namespace {
+
+// Start service method: always throw.
+void start_service(void) {
+    isc_throw(isc::Unexpected, "start service failed");
+};
+
+} // end anonymous
+
+// Functions accessed by the hooks framework use C linkage to avoid the name
+// mangling that accompanies use of the C++ compiler as well as to avoid
+// issues related to namespaces.
+extern "C" {
+
+int
+do_load_impl(LibraryHandle& handle) {
+    // Determine if this callout is configured to fail.
+    isc::dhcp::SrvConfigPtr config;
+    isc::data::ConstElementPtr const& parameters(handle.getParameters());
+    isc::data::ConstElementPtr mode_element(parameters ? parameters->get("mode") : 0);
+    std::string mode(mode_element ? mode_element->stringValue() : "");
+    if (mode == "fail-on-load") {
+        return (1);
+    }
+    return (0);
+}
+
+int (*do_load)(LibraryHandle& handle) = do_load_impl;
+
+int (*do_unload)();
+
+/// @brief Callout which appends library number and provided arguments to
+/// the marker file for dhcp6_srv_configured callout.
+///
+/// @param handle callout handle passed to the callout.
+///
+/// @return 0 on success, 1 otherwise.
+int
+dhcp6_srv_configured(CalloutHandle& handle) {
+
+    // Append library number.
+    if (appendDigit(SRV_CONFIG_MARKER_FILE)) {
+        return (1);
+    }
+
+    // Append argument names.
+    std::vector<std::string> args = handle.getArgumentNames();
+    for (auto const& arg : args) {
+        if (appendArgument(SRV_CONFIG_MARKER_FILE, arg.c_str()) != 0) {
+            return (1);
+        }
+    }
+
+    // Get the IO context to post start_service on it.
+    std::string error("");
+    IOServicePtr io_context;
+    try {
+        handle.getArgument("io_context", io_context);
+        if (!io_context) {
+            error = "null io_context";
+        }
+        io_context->post(start_service);
+    } catch (const std::exception& ex) {
+        error = "no io_context in arguments";
+    }
+    if (!error.empty()) {
+        handle.setArgument("error", error);
+        handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
+    }
+
+    return (0);
+}
+
+/// @brief This function is called to retrieve the multi-threading compatibility.
+///
+/// @return 1 which means compatible with multi-threading.
+int multi_threading_compatible() {
+    return (1);
+}
+
+} // end extern "C"
index 288ae8bd560c7c147a5c994f8ebd43416f988ad0..7013dc8aaa1852ded7a901d783aab68e33751a16 100644 (file)
@@ -5833,4 +5833,66 @@ TEST_F(HooksDhcpv6SrvTest, leases6ParkedPacketLimit) {
     EXPECT_EQ(1, getStatistic("pkt6-receive-drop"));
 }
 
+// Checks that postponed hook start service can fail.
+TEST_F(LoadUnloadDhcpv6SrvTest, StartServiceFail) {
+    boost::shared_ptr<ControlledDhcpv6Srv> srv(new ControlledDhcpv6Srv(0));
+
+    // Ensure no marker files to start with.
+    ASSERT_FALSE(checkMarkerFileExists(LOAD_MARKER_FILE));
+    ASSERT_FALSE(checkMarkerFileExists(UNLOAD_MARKER_FILE));
+    ASSERT_FALSE(checkMarkerFileExists(SRV_CONFIG_MARKER_FILE));
+
+    // Minimal valid configuration for the server. It includes the
+    // section which loads the callout library #4, which implements
+    // dhcp6_srv_configured callout and a failing start service.
+    string config_str =
+        "{"
+        "    \"interfaces-config\": {"
+        "        \"interfaces\": [ ]"
+        "    },"
+        "    \"preferred-lifetime\": 3000,"
+        "    \"rebind-timer\": 2000,"
+        "    \"renew-timer\": 1000,"
+        "    \"subnet6\": [ ],"
+        "    \"valid-lifetime\": 4000,"
+        "    \"lease-database\": {"
+        "         \"type\": \"memfile\","
+        "         \"persist\": false"
+        "    },"
+        "    \"hooks-libraries\": ["
+        "        {"
+        "            \"library\": \"" + std::string(CALLOUT_LIBRARY_4) + "\""
+        "        }"
+        "    ]"
+        "}";
+
+    ConstElementPtr config = Element::fromJSON(config_str);
+
+    // Configure the server.
+    ConstElementPtr answer;
+    ASSERT_NO_THROW(answer = srv->processConfig(config));
+
+    // Make sure there was an error with expected message.
+    int status_code;
+    parseAnswer(status_code, answer);
+    EXPECT_EQ(1, status_code);
+    EXPECT_EQ(answer->str(),
+              R"({ "result": 1, "text": "Error initializing hooks: start service failed" })");
+
+    // The hook library should have been loaded.
+    EXPECT_TRUE(checkMarkerFile(LOAD_MARKER_FILE, "4"));
+    EXPECT_FALSE(checkMarkerFileExists(UNLOAD_MARKER_FILE));
+    // The dhcp6_srv_configured should have been invoked and the provided
+    // parameters should be recorded.
+    EXPECT_TRUE(checkMarkerFile(SRV_CONFIG_MARKER_FILE,
+                                "4io_contextjson_confignetwork_stateserver_config"));
+
+    // Destroy the server, instance which should unload the libraries.
+    srv.reset();
+
+    // The server was destroyed, so the unload() function should now
+    // include the library number in its marker file.
+    EXPECT_TRUE(checkMarkerFile(UNLOAD_MARKER_FILE, "4"));
+}
+
 }  // namespace
index 95d74592ad4bbd5a6cae2263807195c01d3b57ec..09e8a6282042cc2c7462215e728d1d7bd7c0c16d 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2013-2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-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
@@ -20,6 +20,7 @@ namespace {
 const char* const CALLOUT_LIBRARY_1 = "@abs_builddir@/.libs/libco1.so";
 const char* const CALLOUT_LIBRARY_2 = "@abs_builddir@/.libs/libco2.so";
 const char* const CALLOUT_LIBRARY_3 = "@abs_builddir@/.libs/libco3.so";
+const char* const CALLOUT_LIBRARY_4 = "@abs_builddir@/.libs/libco4.so";
 
 // Name of a library which is not present.
 const char* const NOT_PRESENT_LIBRARY = "@abs_builddir@/.libs/libnothere.so";