Browse Source

[5134] Unit-tests implemented.

Tomek Mrugalski 8 years ago
parent
commit
bed54a8d23

+ 1 - 0
configure.ac

@@ -1638,6 +1638,7 @@ AC_CONFIG_FILES([compatcheck/Makefile
                  src/bin/admin/tests/mysql_tests.sh
                  src/bin/admin/tests/mysql_tests.sh
                  src/bin/admin/tests/pgsql_tests.sh
                  src/bin/admin/tests/pgsql_tests.sh
                  src/bin/admin/tests/cql_tests.sh
                  src/bin/admin/tests/cql_tests.sh
+		 src/bin/agent/tests/test_libraries.h
                  src/hooks/Makefile
                  src/hooks/Makefile
                  src/hooks/dhcp/Makefile
                  src/hooks/dhcp/Makefile
                  src/hooks/dhcp/user_chk/Makefile
                  src/hooks/dhcp/user_chk/Makefile

+ 3 - 4
src/bin/agent/ctrl_agent_cfg_mgr.cc

@@ -82,6 +82,7 @@ CtrlAgentCfgMgr::createNewContext() {
 
 
 isc::data::ConstElementPtr
 isc::data::ConstElementPtr
 CtrlAgentCfgMgr::parse(isc::data::ConstElementPtr config_set, bool check_only) {
 CtrlAgentCfgMgr::parse(isc::data::ConstElementPtr config_set, bool check_only) {
+    // Do a sanity check first.
     if (!config_set) {
     if (!config_set) {
         isc_throw(DhcpConfigError, "Mandatory config parameter not provided");
         isc_throw(DhcpConfigError, "Mandatory config parameter not provided");
     }
     }
@@ -92,6 +93,7 @@ CtrlAgentCfgMgr::parse(isc::data::ConstElementPtr config_set, bool check_only) {
     ElementPtr cfg = boost::const_pointer_cast<Element>(config_set);
     ElementPtr cfg = boost::const_pointer_cast<Element>(config_set);
     AgentSimpleParser::setAllDefaults(cfg);
     AgentSimpleParser::setAllDefaults(cfg);
 
 
+    // And parse the configuration.
     ConstElementPtr answer;
     ConstElementPtr answer;
     std::string excuse;
     std::string excuse;
     try {
     try {
@@ -106,6 +108,7 @@ CtrlAgentCfgMgr::parse(isc::data::ConstElementPtr config_set, bool check_only) {
         answer = isc::config::createAnswer(2, excuse);
         answer = isc::config::createAnswer(2, excuse);
     }
     }
 
 
+    // At this stage the answer was created only in case of exception.
     if (answer) {
     if (answer) {
         if (check_only) {
         if (check_only) {
             LOG_ERROR(agent_logger, CTRL_AGENT_CONFIG_CHECK_FAIL).arg(excuse);
             LOG_ERROR(agent_logger, CTRL_AGENT_CONFIG_CHECK_FAIL).arg(excuse);
@@ -117,12 +120,8 @@ CtrlAgentCfgMgr::parse(isc::data::ConstElementPtr config_set, bool check_only) {
 
 
     if (check_only) {
     if (check_only) {
         answer = isc::config::createAnswer(0, "Configuration check successful");
         answer = isc::config::createAnswer(0, "Configuration check successful");
-        LOG_INFO(agent_logger, CTRL_AGENT_CONFIG_CHECK_COMPLETE)
-            .arg(getConfigSummary(0));
     } else {
     } else {
         answer = isc::config::createAnswer(0, "Configuration applied successfully.");
         answer = isc::config::createAnswer(0, "Configuration applied successfully.");
-        LOG_INFO(agent_logger, CTRL_AGENT_CONFIG_COMPLETE)
-            .arg(getConfigSummary(0));
     }
     }
 
 
     return (answer);
     return (answer);

+ 1 - 1
src/bin/agent/ctrl_agent_controller.cc

@@ -16,7 +16,7 @@ namespace agent {
 
 
 /// @brief Defines the application name, this is passed into base class
 /// @brief Defines the application name, this is passed into base class
 /// it may be used to locate configuration data and appears in log statement.
 /// it may be used to locate configuration data and appears in log statement.
-const char* CtrlAgentController::agent_app_name_ = "CtrlAgent";
+const char* CtrlAgentController::agent_app_name_ = "Control-agent";
 
 
 /// @brief Defines the executable name. This is passed into the base class
 /// @brief Defines the executable name. This is passed into the base class
 const char* CtrlAgentController::agent_bin_name_ = "kea-ctrl-agent";
 const char* CtrlAgentController::agent_bin_name_ = "kea-ctrl-agent";

+ 0 - 8
src/bin/agent/ctrl_agent_messages.mes

@@ -19,19 +19,11 @@ This informational message indicates that the DHCP-DDNS server has
 processed all configuration information and is ready to begin processing.
 processed all configuration information and is ready to begin processing.
 The version is also printed.
 The version is also printed.
 
 
-% CTRL_AGENT_CONFIG_COMPLETE Control Agent configuration complete: %1
-This informational message indicates that the CA had completed its
-configuration.
-
 % CTRL_AGENT_CONFIG_FAIL Control Agent configuration failed: %1
 % CTRL_AGENT_CONFIG_FAIL Control Agent configuration failed: %1
 This error message indicates that the CA had failed configuration
 This error message indicates that the CA had failed configuration
 attempt. Details are provided. Additional details may be available
 attempt. Details are provided. Additional details may be available
 in earlier log entries, possibly on lower levels.
 in earlier log entries, possibly on lower levels.
 
 
-% CTRL_AGENT_CONFIG_CHECK_COMPLETE Control Agent configuration check complete: %1
-This informationnal message indicates that the CA had completed the
-configuration check. The outcome of this check is part of the message.
-
 % CTRL_AGENT_CONFIG_CHECK_FAIL Control Agent configuration check failed: %1
 % CTRL_AGENT_CONFIG_CHECK_FAIL Control Agent configuration check failed: %1
 This error message indicates that the CA had failed configuration
 This error message indicates that the CA had failed configuration
 check. Details are provided. Additional details may be available
 check. Details are provided. Additional details may be available

+ 17 - 17
src/bin/agent/simple_parser.cc

@@ -33,7 +33,7 @@ namespace agent {
 ///
 ///
 /// These are global Control Agent parameters.
 /// These are global Control Agent parameters.
 const SimpleDefaults AgentSimpleParser::AGENT_DEFAULTS = {
 const SimpleDefaults AgentSimpleParser::AGENT_DEFAULTS = {
-    { "http-post",    Element::string,  "localhost"},
+    { "http-host",    Element::string,  "localhost"},
     { "http-port",    Element::integer,  "8000"}
     { "http-port",    Element::integer,  "8000"}
 };
 };
 
 
@@ -81,32 +81,32 @@ void
 AgentSimpleParser::parse(CtrlAgentCfgContextPtr ctx, isc::data::ConstElementPtr config,
 AgentSimpleParser::parse(CtrlAgentCfgContextPtr ctx, isc::data::ConstElementPtr config,
                          bool check_only) {
                          bool check_only) {
 
 
+    // Let's get the HTTP parameters first.
     ctx->setHost(SimpleParser::getString(config, "http-host"));
     ctx->setHost(SimpleParser::getString(config, "http-host"));
     ctx->setPort(SimpleParser::getIntType<uint16_t>(config, "http-port"));
     ctx->setPort(SimpleParser::getIntType<uint16_t>(config, "http-port"));
 
 
+    // Control sockets are second.
     ConstElementPtr ctrl_sockets = config->get("control-sockets");
     ConstElementPtr ctrl_sockets = config->get("control-sockets");
-    if (!ctrl_sockets) {
-        isc_throw(ConfigError, "Missing mandatory parameter 'control-sockets'");
-    }
-
-    ConstElementPtr d2_socket = ctrl_sockets->get("d2-server");
-    ConstElementPtr d4_socket = ctrl_sockets->get("dhcp4-server");
-    ConstElementPtr d6_socket = ctrl_sockets->get("dhcp6-server");
+    if (ctrl_sockets) {
+        ConstElementPtr d2_socket = ctrl_sockets->get("d2-server");
+        ConstElementPtr d4_socket = ctrl_sockets->get("dhcp4-server");
+        ConstElementPtr d6_socket = ctrl_sockets->get("dhcp6-server");
 
 
-    if (d2_socket) {
-        ctx->setControlSocketInfo(d2_socket, CtrlAgentCfgContext::TYPE_D2);
-    }
+        if (d2_socket) {
+            ctx->setControlSocketInfo(d2_socket, CtrlAgentCfgContext::TYPE_D2);
+        }
 
 
-    if (d4_socket) {
-        ctx->setControlSocketInfo(d4_socket, CtrlAgentCfgContext::TYPE_DHCP4);
-    }
+        if (d4_socket) {
+            ctx->setControlSocketInfo(d4_socket, CtrlAgentCfgContext::TYPE_DHCP4);
+        }
 
 
-    if (d6_socket) {
-        ctx->setControlSocketInfo(d6_socket, CtrlAgentCfgContext::TYPE_DHCP6);
+        if (d6_socket) {
+            ctx->setControlSocketInfo(d6_socket, CtrlAgentCfgContext::TYPE_DHCP6);
+        }
     }
     }
 
 
+    // Finally, let's get the hook libs!
     hooks::HooksLibrariesParser hooks_parser;
     hooks::HooksLibrariesParser hooks_parser;
-    
     ConstElementPtr hooks = config->get("hooks-libraries");
     ConstElementPtr hooks = config->get("hooks-libraries");
     if (hooks) {
     if (hooks) {
         hooks_parser.parse(hooks);
         hooks_parser.parse(hooks);

+ 2 - 0
src/bin/agent/tests/Makefile.am

@@ -86,6 +86,8 @@ libbasic_la_LIBADD  += $(top_builddir)/src/lib/hooks/libkea-hooks.la
 libbasic_la_LIBADD  += $(top_builddir)/src/lib/log/libkea-log.la
 libbasic_la_LIBADD  += $(top_builddir)/src/lib/log/libkea-log.la
 libbasic_la_LDFLAGS  = -avoid-version -export-dynamic -module -rpath /nowhere
 libbasic_la_LDFLAGS  = -avoid-version -export-dynamic -module -rpath /nowhere
 
 
+nodist_run_unittests_SOURCES = test_libraries.h
+
 endif
 endif
 
 
 noinst_PROGRAMS = $(TESTS)
 noinst_PROGRAMS = $(TESTS)

+ 258 - 0
src/bin/agent/tests/ctrl_agent_cfg_mgr_unittest.cc

@@ -6,14 +6,24 @@
 
 
 #include <config.h>
 #include <config.h>
 #include <agent/ctrl_agent_cfg_mgr.h>
 #include <agent/ctrl_agent_cfg_mgr.h>
+#include <agent/parser_context.h>
 #include <process/testutils/d_test_stubs.h>
 #include <process/testutils/d_test_stubs.h>
+#include <agent/tests/test_libraries.h>
 #include <boost/scoped_ptr.hpp>
 #include <boost/scoped_ptr.hpp>
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 
 
 using namespace isc::agent;
 using namespace isc::agent;
+using namespace isc::data;
+using namespace isc::hooks;
 
 
 namespace  {
 namespace  {
 
 
+/// @brief Almost regular agent CfgMgr with internal parse method exposed.
+class NakedAgentCfgMgr : public CtrlAgentCfgMgr {
+public:
+    using CtrlAgentCfgMgr::parse;
+};
+
 // Tests construction of CtrlAgentCfgMgr class.
 // Tests construction of CtrlAgentCfgMgr class.
 TEST(CtrlAgentCfgMgr, construction) {
 TEST(CtrlAgentCfgMgr, construction) {
     boost::scoped_ptr<CtrlAgentCfgMgr> cfg_mgr;
     boost::scoped_ptr<CtrlAgentCfgMgr> cfg_mgr;
@@ -30,4 +40,252 @@ TEST(CtrlAgentCfgMgr, construction) {
     EXPECT_NO_THROW(cfg_mgr.reset());
     EXPECT_NO_THROW(cfg_mgr.reset());
 }
 }
 
 
+// Tests if getContext can be retrieved.
+TEST(CtrlAgentCfgMgr, getContext) {
+    CtrlAgentCfgMgr cfg_mgr;
+
+    CtrlAgentCfgContextPtr ctx;
+    ASSERT_NO_THROW(ctx = cfg_mgr.getCtrlAgentCfgContext());
+    ASSERT_TRUE(ctx);
+}
+
+// Tests if context can store and retrieve HTTP parameters
+TEST(CtrlAgentCfgMgr, contextHttpParams) {
+    CtrlAgentCfgContext ctx;
+
+    // Check http parameters
+    ctx.setPort(12345);
+    EXPECT_EQ(12345, ctx.getPort());
+
+    ctx.setHost("alnitak");
+    EXPECT_EQ("alnitak", ctx.getHost());
+}
+
+// Tests if context can store and retrieve control socket information.
+TEST(CtrlAgentCfgMgr, contextSocketInfo) {
+
+    CtrlAgentCfgContext ctx;
+
+    // Check control socket parameters
+    // By default, there are no control sockets stored.
+    EXPECT_FALSE(ctx.getControlSocketInfo(CtrlAgentCfgContext::TYPE_D2));
+    EXPECT_FALSE(ctx.getControlSocketInfo(CtrlAgentCfgContext::TYPE_DHCP4));
+    EXPECT_FALSE(ctx.getControlSocketInfo(CtrlAgentCfgContext::TYPE_DHCP6));
+
+    ConstElementPtr socket1 = Element::fromJSON("{ \"socket-type\": \"unix\",\n"
+                                                "  \"socket-name\": \"socket1\" }");
+    ConstElementPtr socket2 = Element::fromJSON("{ \"socket-type\": \"unix\",\n"
+                                                "  \"socket-name\": \"socket2\" }");
+    ConstElementPtr socket3 = Element::fromJSON("{ \"socket-type\": \"unix\",\n"
+                                                "  \"socket-name\": \"socket3\" }");
+    // Ok, now set the control socket for D2
+    EXPECT_NO_THROW(ctx.setControlSocketInfo(socket1, CtrlAgentCfgContext::TYPE_D2));
+
+    // Now check the values returned
+    EXPECT_EQ(socket1, ctx.getControlSocketInfo(CtrlAgentCfgContext::TYPE_D2));
+    EXPECT_FALSE(ctx.getControlSocketInfo(CtrlAgentCfgContext::TYPE_DHCP4));
+    EXPECT_FALSE(ctx.getControlSocketInfo(CtrlAgentCfgContext::TYPE_DHCP6));
+
+    // Now set the v6 socket and sanity check again
+    EXPECT_NO_THROW(ctx.setControlSocketInfo(socket2, CtrlAgentCfgContext::TYPE_DHCP6));
+
+    // Should be possible to retrieve two sockets.
+    EXPECT_EQ(socket1, ctx.getControlSocketInfo(CtrlAgentCfgContext::TYPE_D2));
+    EXPECT_EQ(socket2, ctx.getControlSocketInfo(CtrlAgentCfgContext::TYPE_DHCP6));
+    EXPECT_FALSE(ctx.getControlSocketInfo(CtrlAgentCfgContext::TYPE_DHCP4));
+
+    // Finally, set the third control socket.
+    EXPECT_NO_THROW(ctx.setControlSocketInfo(socket3, CtrlAgentCfgContext::TYPE_DHCP4));
+    EXPECT_EQ(socket1, ctx.getControlSocketInfo(CtrlAgentCfgContext::TYPE_D2));
+    EXPECT_EQ(socket2, ctx.getControlSocketInfo(CtrlAgentCfgContext::TYPE_DHCP6));
+    EXPECT_EQ(socket3, ctx.getControlSocketInfo(CtrlAgentCfgContext::TYPE_DHCP4));
+}
+
+// Tests if the context can store and retrieve hook libs information.
+TEST(CtrlAgentCfgMgr, contextHookParams) {
+    CtrlAgentCfgContext ctx;
+
+    // By default there should be no hooks.
+    HookLibsCollection libs = ctx.getLibraries();
+    EXPECT_TRUE(libs.empty());
+
+    libs.push_back(std::make_pair("libone.so", ConstElementPtr()));
+    libs.push_back(std::make_pair("libtwo.so", Element::fromJSON("{\"foo\": true}")));
+    libs.push_back(std::make_pair("libthree.so", Element::fromJSON("{\"bar\": 42}")));
+
+    ctx.setLibraries(libs);
+
+    HookLibsCollection stored_libs = ctx.getLibraries();
+    EXPECT_EQ(3, stored_libs.size());
+
+    EXPECT_EQ(libs, stored_libs);
+}
+
+/// Control Agent configurations used in tests.
+const char* AGENT_CONFIGS[] = {
+
+    // configuration 0: empty (nothing specified)
+    "{ }",
+
+    // Configuration 1: http parameters only (no control sockets, not hooks)
+    "{  \"http-host\": \"betelguese\",\n"
+    "    \"http-port\": 8001\n"
+    "}",
+
+    // Configuration 2: http and 1 socket
+    "{\n"
+    "    \"http-host\": \"betelguese\",\n"
+    "    \"http-port\": 8001,\n"
+    "    \"control-sockets\": {\n"
+    "        \"dhcp4-server\": {\n"
+    "            \"socket-name\": \"/tmp/socket-v4\"\n"
+    "        }\n"
+    "    }\n"
+    "}",
+
+    // Configuration 3: http and all 3 sockets
+    "{\n"
+    "    \"http-host\": \"betelguese\",\n"
+    "    \"http-port\": 8001,\n"
+    "    \"control-sockets\": {\n"
+    "        \"dhcp4-server\": {\n"
+    "            \"socket-name\": \"/tmp/socket-v4\"\n"
+    "        },\n"
+    "        \"dhcp6-server\": {\n"
+    "            \"socket-name\": \"/tmp/socket-v6\"\n"
+    "        },\n"
+    "        \"d2-server\": {\n"
+    "            \"socket-name\": \"/tmp/socket-d2\"\n"
+    "        }\n"
+    "   }\n"
+    "}",
+
+    // Configuration 4: http, 1 socket and hooks
+    // CA is able to load hook libraries that augment its operation.
+    // The primary functionality is the ability to add new commands.
+    "{\n"
+    "    \"http-host\": \"betelguese\",\n"
+    "    \"http-port\": 8001,\n"
+    "    \"control-sockets\": {\n"
+    "        \"dhcp4-server\": {\n"
+    "            \"socket-name\": \"/tmp/socket-v4\"\n"
+    "        }\n"
+    "   },\n"
+    "    \"hooks-libraries\": ["
+    "        {"
+    "          \"library\": \"%LIBRARY%\","
+    "              \"parameters\": {\n"
+    "              \"param1\": \"foo\"\n"
+    "            }\n"
+    "        }\n"
+    "     ]\n"
+    "}"
+};
+
+/// @brief Class used for testing CfgMgr
+class AgentParserTest : public isc::process::ConfigParseTest {
+public:
+
+    /// @brief Tries to load input text as a configuration
+    ///
+    /// @param config text containing input configuration
+    /// @param expected_answer expected result of configuration (0 = success)
+    void configParse(const char* config, int expected_answer) {
+        isc::agent::ParserContext parser;
+        ConstElementPtr json = parser.parseString(config, ParserContext::PARSER_SUB_AGENT);
+
+        EXPECT_NO_THROW(answer_ = cfg_mgr_.parse(json, false));
+        EXPECT_TRUE(checkAnswer(expected_answer));
+    }
+
+    /// @brief Reeplaces %LIBRARY% with specified library name
+    ///
+    /// @param config input config text (should contain "%LIBRARY%" string)
+    /// @param lib_name %LIBRARY% will be replaced with that name
+    /// @return configuration text with library name replaced
+    std::string pathReplacer(const char* config, const char* lib_name) {
+        string txt(config);
+        txt.replace(txt.find("%LIBRARY%"), strlen("%LIBRARY%"), string(lib_name));
+        return (txt);
+    }
+
+    /// Configuration Manager (used in tests)
+    NakedAgentCfgMgr cfg_mgr_;
+};
+
+// This test verifies if an empty config is handled properly. In practice such
+// a config makes little sense, but perhaps it's ok for a default deployment.
+// Sadly, our bison parser requires at last one parameter to be present.
+// Until we determine whether we want the empty config to be allowed or not,
+// this test remains disabled.
+TEST_F(AgentParserTest, DISABLED_configParseEmpty) {
+    configParse(AGENT_CONFIGS[0], 0);
+}
+
+// This test checks if a config with only HTTP parameters is parsed properly.
+TEST_F(AgentParserTest, configParseHttpOnly) {
+    configParse(AGENT_CONFIGS[1], 0);
+
+    CtrlAgentCfgContextPtr ctx = cfg_mgr_.getCtrlAgentCfgContext();
+    ASSERT_TRUE(ctx);
+    EXPECT_EQ("betelguese", ctx->getHost());
+    EXPECT_EQ(8001, ctx->getPort());
+}
+
+// Tests if a single socket can be configured. BTW this test also checks
+// if default value for socket-type is specified (the config doesn't have it,
+// so the default value should be filed in).
+TEST_F(AgentParserTest, configParse1Socket) {
+    configParse(AGENT_CONFIGS[2], 0);
+
+    CtrlAgentCfgContextPtr ctx = cfg_mgr_.getCtrlAgentCfgContext();
+    ASSERT_TRUE(ctx);
+    ConstElementPtr socket = ctx->getControlSocketInfo(CtrlAgentCfgContext::TYPE_DHCP4);
+    ASSERT_TRUE(socket);
+    EXPECT_EQ("{ \"socket-name\": \"/tmp/socket-v4\", \"socket-type\": \"unix\" }",
+              socket->str());
+    EXPECT_FALSE(ctx->getControlSocketInfo(CtrlAgentCfgContext::TYPE_DHCP6));
+    EXPECT_FALSE(ctx->getControlSocketInfo(CtrlAgentCfgContext::TYPE_D2));
+}
+
+// This tests if all 3 sockets can be configured and makes sure the parser
+// doesn't confuse them.
+TEST_F(AgentParserTest, configParse3Sockets) {
+    configParse(AGENT_CONFIGS[3], 0);
+    CtrlAgentCfgContextPtr ctx = cfg_mgr_.getCtrlAgentCfgContext();
+    ASSERT_TRUE(ctx);
+    ConstElementPtr socket2 = ctx->getControlSocketInfo(CtrlAgentCfgContext::TYPE_D2);
+    ConstElementPtr socket4 = ctx->getControlSocketInfo(CtrlAgentCfgContext::TYPE_DHCP4);
+    ConstElementPtr socket6 = ctx->getControlSocketInfo(CtrlAgentCfgContext::TYPE_DHCP6);
+    ASSERT_TRUE(socket2);
+    EXPECT_EQ("{ \"socket-name\": \"/tmp/socket-d2\", \"socket-type\": \"unix\" }",
+              socket2->str());
+    ASSERT_TRUE(socket4);
+    EXPECT_EQ("{ \"socket-name\": \"/tmp/socket-v4\", \"socket-type\": \"unix\" }",
+              socket4->str());
+    ASSERT_TRUE(socket6);
+    EXPECT_EQ("{ \"socket-name\": \"/tmp/socket-v6\", \"socket-type\": \"unix\" }",
+              socket6->str());
+}
+
+// This test checks that the config file with hook library specified can be
+// loaded. This one is a bit tricky, because the parser sanity checks the lib
+// name. In particular, it checks if such a library exists. Therefore we
+// can't use AGENT_CONFIGS[4] as is, but need to run it through path replacer.
+TEST_F(AgentParserTest, configParseHooks) {
+    // Create the configuration with proper lib path.
+    std::string cfg = pathReplacer(AGENT_CONFIGS[4], BASIC_CALLOUT_LIBRARY);
+    // The configuration should be successful.
+    configParse(cfg.c_str(), 0);
+
+    // The context now should have the library specified.
+    CtrlAgentCfgContextPtr ctx = cfg_mgr_.getCtrlAgentCfgContext();
+    HookLibsCollection libs = ctx->getLibraries();
+    ASSERT_EQ(1, libs.size());
+    EXPECT_EQ(string(BASIC_CALLOUT_LIBRARY), libs[0].first);
+    ASSERT_TRUE(libs[0].second);
+    EXPECT_EQ("{ \"param1\": \"foo\" }", libs[0].second->str());
 }
 }
+
+
+}; // end of anonymous namespace

+ 2 - 2
src/bin/agent/tests/ctrl_agent_process_tests.sh.in

@@ -14,7 +14,7 @@ EXPECTED_VERSION="@PACKAGE_VERSION@"
 # Control Agent configuration to be stored in the configuration file.
 # Control Agent configuration to be stored in the configuration file.
 # todo: use actual configuration once we support it.
 # todo: use actual configuration once we support it.
 CONFIG="{
 CONFIG="{
-    \"CtrlAgent\":
+    \"Control-agent\":
     {
     {
         \"dummy-param\": 123
         \"dummy-param\": 123
     },
     },
@@ -75,7 +75,7 @@ shutdown_test() {
     # It should be just once on startup.
     # It should be just once on startup.
     get_reconfigs
     get_reconfigs
     if [ ${_GET_RECONFIGS} -ne 1 ]; then
     if [ ${_GET_RECONFIGS} -ne 1 ]; then
-        printf "ERROR: server hasn't been configured.\n"
+        printf "ERROR: server been configured ${_GET_RECONFIGS} time(s), but exactly 1 was expected.\n"
         clean_exit 1
         clean_exit 1
     else
     else
         printf "Server successfully configured.\n"
         printf "Server successfully configured.\n"

+ 24 - 0
src/bin/agent/tests/test_libraries.h.in

@@ -0,0 +1,24 @@
+// Copyright (C) 2017 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 AGENT_TEST_LIBRARIES_H
+#define AGENT_TEST_LIBRARIES_H
+
+#include <config.h>
+
+namespace {
+
+// Names of the libraries used in these tests.  These libraries are built using
+// libtool, so we need to look in the hidden ".libs" directory to locate the
+// .so file.  Note that we access the .so file - libtool creates this as a
+// like to the real shared library.
+
+// Basic library with context_create and three "standard" callouts.
+static const char* BASIC_CALLOUT_LIBRARY = "@abs_builddir@/.libs/libbasic.so";
+
+} // anonymous namespace
+
+#endif // TEST_LIBRARIES_H