Skip to content

Commit 09d42d8

Browse files
committed
[#2692] Added unit tests
1 parent dd8ebba commit 09d42d8

File tree

8 files changed

+349
-4
lines changed

8 files changed

+349
-4
lines changed

src/bin/dhcp4/tests/Makefile.am

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,13 @@ libco3_la_CXXFLAGS = $(AM_CXXFLAGS)
6464
libco3_la_CPPFLAGS = $(AM_CPPFLAGS)
6565
libco3_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
6666

67+
libco4_la_SOURCES = callout_library_4.cc callout_library_common.h
68+
libco4_la_CXXFLAGS = $(AM_CXXFLAGS)
69+
libco4_la_CPPFLAGS = $(AM_CPPFLAGS)
70+
libco4_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
71+
6772
# Don't install test libraries.
68-
noinst_LTLIBRARIES = libco1.la libco2.la libco3.la
73+
noinst_LTLIBRARIES = libco1.la libco2.la libco3.la libco4.la
6974

7075
# C++ tests
7176
PROGRAM_TESTS = dhcp4_unittests
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Copyright (C) 2018-2024 Internet Systems Consortium, Inc. ("ISC")
2+
//
3+
// This Source Code Form is subject to the terms of the Mozilla Public
4+
// License, v. 2.0. If a copy of the MPL was not distributed with this
5+
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
7+
/// @file
8+
/// @brief Callout library for testing execution of the dhcp4_srv_configured
9+
/// hook point.
10+
///
11+
static const int LIBRARY_NUMBER = 4;
12+
13+
#include <config.h>
14+
15+
#include <asiolink/io_service.h>
16+
#include <dhcp4/tests/callout_library_common.h>
17+
#include <dhcpsrv/srv_config.h>
18+
19+
#include <string>
20+
#include <vector>
21+
22+
using namespace isc::asiolink;
23+
using namespace isc::hooks;
24+
25+
namespace {
26+
27+
// Start service method: always throw.
28+
void start_service(void) {
29+
isc_throw(isc::Unexpected, "start service failed");
30+
};
31+
32+
} // end anonymous
33+
34+
// Functions accessed by the hooks framework use C linkage to avoid the name
35+
// mangling that accompanies use of the C++ compiler as well as to avoid
36+
// issues related to namespaces.
37+
extern "C" {
38+
39+
int
40+
do_load_impl(LibraryHandle& handle) {
41+
// Determine if this callout is configured to fail.
42+
isc::dhcp::SrvConfigPtr config;
43+
isc::data::ConstElementPtr const& parameters(handle.getParameters());
44+
isc::data::ConstElementPtr mode_element(parameters ? parameters->get("mode") : 0);
45+
std::string mode(mode_element ? mode_element->stringValue() : "");
46+
if (mode == "fail-on-load") {
47+
return (1);
48+
}
49+
return (0);
50+
}
51+
52+
int (*do_load)(LibraryHandle& handle) = do_load_impl;
53+
54+
int (*do_unload)();
55+
56+
/// @brief Callout which appends library number and provided arguments to
57+
/// the marker file for dhcp4_srv_configured callout.
58+
///
59+
/// @param handle callout handle passed to the callout.
60+
///
61+
/// @return 0 on success, 1 otherwise.
62+
int
63+
dhcp4_srv_configured(CalloutHandle& handle) {
64+
65+
// Append library number.
66+
if (appendDigit(SRV_CONFIG_MARKER_FILE)) {
67+
return (1);
68+
}
69+
70+
// Append argument names.
71+
std::vector<std::string> args = handle.getArgumentNames();
72+
for (auto const& arg : args) {
73+
if (appendArgument(SRV_CONFIG_MARKER_FILE, arg.c_str()) != 0) {
74+
return (1);
75+
}
76+
}
77+
78+
// Get the IO context to post start_service on it.
79+
std::string error("");
80+
IOServicePtr io_context;
81+
try {
82+
handle.getArgument("io_context", io_context);
83+
if (!io_context) {
84+
error = "null io_context";
85+
}
86+
io_context->post(start_service);
87+
} catch (const std::exception& ex) {
88+
error = "no io_context in arguments";
89+
}
90+
if (!error.empty()) {
91+
handle.setArgument("error", error);
92+
handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
93+
}
94+
95+
return (0);
96+
}
97+
98+
/// @brief This function is called to retrieve the multi-threading compatibility.
99+
///
100+
/// @return 1 which means compatible with multi-threading.
101+
int multi_threading_compatible() {
102+
return (1);
103+
}
104+
105+
} // end extern "C"

src/bin/dhcp4/tests/hooks_unittest.cc

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4039,4 +4039,65 @@ TEST_F(HooksDhcpv4SrvTest, lease4OfferDiscoverDecline) {
40394039
EXPECT_EQ(expected_address, callback_lease4_->addr_);
40404040
}
40414041

4042+
// Checks that postponed hook start service can fail.
4043+
TEST_F(LoadUnloadDhcpv4SrvTest, StartServiceFail) {
4044+
boost::shared_ptr<ControlledDhcpv4Srv> srv(new ControlledDhcpv4Srv(0));
4045+
4046+
// Ensure no marker files to start with.
4047+
ASSERT_FALSE(checkMarkerFileExists(LOAD_MARKER_FILE));
4048+
ASSERT_FALSE(checkMarkerFileExists(UNLOAD_MARKER_FILE));
4049+
ASSERT_FALSE(checkMarkerFileExists(SRV_CONFIG_MARKER_FILE));
4050+
4051+
// Minimal valid configuration for the server. It includes the
4052+
// section which loads the callout library #4, which implements
4053+
// dhcp4_srv_configured callout and a failing start service.
4054+
string config_str =
4055+
"{"
4056+
" \"interfaces-config\": {"
4057+
" \"interfaces\": [ ]"
4058+
" },"
4059+
" \"rebind-timer\": 2000,"
4060+
" \"renew-timer\": 1000,"
4061+
" \"subnet4\": [ ],"
4062+
" \"valid-lifetime\": 4000,"
4063+
" \"lease-database\": {"
4064+
" \"type\": \"memfile\","
4065+
" \"persist\": false"
4066+
" },"
4067+
" \"hooks-libraries\": ["
4068+
" {"
4069+
" \"library\": \"" + std::string(CALLOUT_LIBRARY_4) + "\""
4070+
" }"
4071+
" ]"
4072+
"}";
4073+
4074+
ConstElementPtr config = Element::fromJSON(config_str);
4075+
4076+
// Configure the server.
4077+
ConstElementPtr answer;
4078+
ASSERT_NO_THROW(answer = srv->processConfig(config));
4079+
4080+
// Make sure there was an error with expected message.
4081+
int status_code;
4082+
parseAnswer(status_code, answer);
4083+
EXPECT_EQ(1, status_code);
4084+
EXPECT_EQ(answer->str(),
4085+
R"({ "result": 1, "text": "Error initializing hooks: start service failed" })");
4086+
4087+
// The hook library should have been loaded.
4088+
EXPECT_TRUE(checkMarkerFile(LOAD_MARKER_FILE, "4"));
4089+
EXPECT_FALSE(checkMarkerFileExists(UNLOAD_MARKER_FILE));
4090+
// The dhcp4_srv_configured should have been invoked and the provided
4091+
// parameters should be recorded.
4092+
EXPECT_TRUE(checkMarkerFile(SRV_CONFIG_MARKER_FILE,
4093+
"4io_contextjson_confignetwork_stateserver_config"));
4094+
4095+
// Destroy the server, instance which should unload the libraries.
4096+
srv.reset();
4097+
4098+
// The server was destroyed, so the unload() function should now
4099+
// include the library number in its marker file.
4100+
EXPECT_TRUE(checkMarkerFile(UNLOAD_MARKER_FILE, "4"));
4101+
}
4102+
40424103
} // namespace

src/bin/dhcp4/tests/test_libraries.h.in

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (C) 2013-2018 Internet Systems Consortium, Inc. ("ISC")
1+
// Copyright (C) 2013-2024 Internet Systems Consortium, Inc. ("ISC")
22
//
33
// This Source Code Form is subject to the terms of the Mozilla Public
44
// License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -22,6 +22,7 @@ namespace {
2222
const char* const CALLOUT_LIBRARY_1 = "@abs_builddir@/.libs/libco1.so";
2323
const char* const CALLOUT_LIBRARY_2 = "@abs_builddir@/.libs/libco2.so";
2424
const char* const CALLOUT_LIBRARY_3 = "@abs_builddir@/.libs/libco3.so";
25+
const char* const CALLOUT_LIBRARY_4 = "@abs_builddir@/.libs/libco4.so";
2526

2627
// Name of a library which is not present.
2728
const char* const NOT_PRESENT_LIBRARY = "@abs_builddir@/.libs/libnothere.so";

src/bin/dhcp6/tests/Makefile.am

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,13 @@ libco3_la_CXXFLAGS = $(AM_CXXFLAGS)
6565
libco3_la_CPPFLAGS = $(AM_CPPFLAGS)
6666
libco3_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
6767

68+
libco4_la_SOURCES = callout_library_4.cc callout_library_common.h
69+
libco4_la_CXXFLAGS = $(AM_CXXFLAGS)
70+
libco4_la_CPPFLAGS = $(AM_CPPFLAGS)
71+
libco4_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
72+
6873
# Don't install test libraries.
69-
noinst_LTLIBRARIES = libco1.la libco2.la libco3.la
74+
noinst_LTLIBRARIES = libco1.la libco2.la libco3.la libco4.la
7075

7176
# C++ tests
7277
PROGRAM_TESTS = dhcp6_unittests
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Copyright (C) 2018-2024 Internet Systems Consortium, Inc. ("ISC")
2+
//
3+
// This Source Code Form is subject to the terms of the Mozilla Public
4+
// License, v. 2.0. If a copy of the MPL was not distributed with this
5+
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
7+
/// @file
8+
/// @brief Callout library for testing execution of the dhcp6_srv_configured
9+
/// hook point.
10+
///
11+
static const int LIBRARY_NUMBER = 4;
12+
13+
#include <config.h>
14+
15+
#include <asiolink/io_service.h>
16+
#include <dhcp6/tests/callout_library_common.h>
17+
#include <dhcpsrv/srv_config.h>
18+
19+
#include <string>
20+
#include <vector>
21+
22+
using namespace isc::asiolink;
23+
using namespace isc::hooks;
24+
25+
namespace {
26+
27+
// Start service method: always throw.
28+
void start_service(void) {
29+
isc_throw(isc::Unexpected, "start service failed");
30+
};
31+
32+
} // end anonymous
33+
34+
// Functions accessed by the hooks framework use C linkage to avoid the name
35+
// mangling that accompanies use of the C++ compiler as well as to avoid
36+
// issues related to namespaces.
37+
extern "C" {
38+
39+
int
40+
do_load_impl(LibraryHandle& handle) {
41+
// Determine if this callout is configured to fail.
42+
isc::dhcp::SrvConfigPtr config;
43+
isc::data::ConstElementPtr const& parameters(handle.getParameters());
44+
isc::data::ConstElementPtr mode_element(parameters ? parameters->get("mode") : 0);
45+
std::string mode(mode_element ? mode_element->stringValue() : "");
46+
if (mode == "fail-on-load") {
47+
return (1);
48+
}
49+
return (0);
50+
}
51+
52+
int (*do_load)(LibraryHandle& handle) = do_load_impl;
53+
54+
int (*do_unload)();
55+
56+
/// @brief Callout which appends library number and provided arguments to
57+
/// the marker file for dhcp6_srv_configured callout.
58+
///
59+
/// @param handle callout handle passed to the callout.
60+
///
61+
/// @return 0 on success, 1 otherwise.
62+
int
63+
dhcp6_srv_configured(CalloutHandle& handle) {
64+
65+
// Append library number.
66+
if (appendDigit(SRV_CONFIG_MARKER_FILE)) {
67+
return (1);
68+
}
69+
70+
// Append argument names.
71+
std::vector<std::string> args = handle.getArgumentNames();
72+
for (auto const& arg : args) {
73+
if (appendArgument(SRV_CONFIG_MARKER_FILE, arg.c_str()) != 0) {
74+
return (1);
75+
}
76+
}
77+
78+
// Get the IO context to post start_service on it.
79+
std::string error("");
80+
IOServicePtr io_context;
81+
try {
82+
handle.getArgument("io_context", io_context);
83+
if (!io_context) {
84+
error = "null io_context";
85+
}
86+
io_context->post(start_service);
87+
} catch (const std::exception& ex) {
88+
error = "no io_context in arguments";
89+
}
90+
if (!error.empty()) {
91+
handle.setArgument("error", error);
92+
handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
93+
}
94+
95+
return (0);
96+
}
97+
98+
/// @brief This function is called to retrieve the multi-threading compatibility.
99+
///
100+
/// @return 1 which means compatible with multi-threading.
101+
int multi_threading_compatible() {
102+
return (1);
103+
}
104+
105+
} // end extern "C"

src/bin/dhcp6/tests/hooks_unittest.cc

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5833,4 +5833,66 @@ TEST_F(HooksDhcpv6SrvTest, leases6ParkedPacketLimit) {
58335833
EXPECT_EQ(1, getStatistic("pkt6-receive-drop"));
58345834
}
58355835

5836+
// Checks that postponed hook start service can fail.
5837+
TEST_F(LoadUnloadDhcpv6SrvTest, StartServiceFail) {
5838+
boost::shared_ptr<ControlledDhcpv6Srv> srv(new ControlledDhcpv6Srv(0));
5839+
5840+
// Ensure no marker files to start with.
5841+
ASSERT_FALSE(checkMarkerFileExists(LOAD_MARKER_FILE));
5842+
ASSERT_FALSE(checkMarkerFileExists(UNLOAD_MARKER_FILE));
5843+
ASSERT_FALSE(checkMarkerFileExists(SRV_CONFIG_MARKER_FILE));
5844+
5845+
// Minimal valid configuration for the server. It includes the
5846+
// section which loads the callout library #4, which implements
5847+
// dhcp6_srv_configured callout and a failing start service.
5848+
string config_str =
5849+
"{"
5850+
" \"interfaces-config\": {"
5851+
" \"interfaces\": [ ]"
5852+
" },"
5853+
" \"preferred-lifetime\": 3000,"
5854+
" \"rebind-timer\": 2000,"
5855+
" \"renew-timer\": 1000,"
5856+
" \"subnet6\": [ ],"
5857+
" \"valid-lifetime\": 4000,"
5858+
" \"lease-database\": {"
5859+
" \"type\": \"memfile\","
5860+
" \"persist\": false"
5861+
" },"
5862+
" \"hooks-libraries\": ["
5863+
" {"
5864+
" \"library\": \"" + std::string(CALLOUT_LIBRARY_4) + "\""
5865+
" }"
5866+
" ]"
5867+
"}";
5868+
5869+
ConstElementPtr config = Element::fromJSON(config_str);
5870+
5871+
// Configure the server.
5872+
ConstElementPtr answer;
5873+
ASSERT_NO_THROW(answer = srv->processConfig(config));
5874+
5875+
// Make sure there was an error with expected message.
5876+
int status_code;
5877+
parseAnswer(status_code, answer);
5878+
EXPECT_EQ(1, status_code);
5879+
EXPECT_EQ(answer->str(),
5880+
R"({ "result": 1, "text": "Error initializing hooks: start service failed" })");
5881+
5882+
// The hook library should have been loaded.
5883+
EXPECT_TRUE(checkMarkerFile(LOAD_MARKER_FILE, "4"));
5884+
EXPECT_FALSE(checkMarkerFileExists(UNLOAD_MARKER_FILE));
5885+
// The dhcp6_srv_configured should have been invoked and the provided
5886+
// parameters should be recorded.
5887+
EXPECT_TRUE(checkMarkerFile(SRV_CONFIG_MARKER_FILE,
5888+
"4io_contextjson_confignetwork_stateserver_config"));
5889+
5890+
// Destroy the server, instance which should unload the libraries.
5891+
srv.reset();
5892+
5893+
// The server was destroyed, so the unload() function should now
5894+
// include the library number in its marker file.
5895+
EXPECT_TRUE(checkMarkerFile(UNLOAD_MARKER_FILE, "4"));
5896+
}
5897+
58365898
} // namespace

0 commit comments

Comments
 (0)