Commit 85918740 authored by Francis Dupont's avatar Francis Dupont

[5152] Done (at the exception of unit tests)

parent a6d16a9d
This diff is collapsed.
......@@ -52,7 +52,8 @@
<arg><option>-V</option></arg>
<arg><option>-W</option></arg>
<arg><option>-d</option></arg>
<arg><option>-s</option></arg>
<arg><option>-c<replaceable class="parameter">config-file</replaceable></option></arg>
<arg><option>-t<replaceable class="parameter">config-file</replaceable></option></arg>
</cmdsynopsis>
</refsynopsisdiv>
......@@ -115,6 +116,16 @@
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>-t</option></term>
<listitem><para>
Check the syntax of the configuration file and report the
first error if any. Note that not all parameters are
completely checked, in particular, service and client
sockets are not opened, and hook libraries are not loaded.
</para></listitem>
</varlistentry>
</variablelist>
</refsect1>
......
......@@ -25,9 +25,18 @@ int main(int argc, char* argv[]) {
// 'false' value disables test mode.
controller->launch(argc, argv, false);
} catch (const VersionMessage& ex) {
std::cout << ex.what() << std::endl;
std::string msg(ex.what());
if (!msg.empty()) {
std::cout << msg << std::endl;
}
} catch (const InvalidUsage& ex) {
std::string msg(ex.what());
if (!msg.empty()) {
std::cerr << msg << std::endl;
}
ret = EXIT_FAILURE;
} catch (const isc::Exception& ex) {
std::cerr << "Service failed:" << ex.what() << std::endl;
std::cerr << "Service failed: " << ex.what() << std::endl;
ret = EXIT_FAILURE;
}
......
......@@ -195,14 +195,14 @@ D2Process::configure(isc::data::ConstElementPtr config_set, bool check_only) {
LOG_DEBUG(d2_logger, DBGLVL_TRACE_BASIC,
DHCP_DDNS_CONFIGURE).arg(config_set->str());
/// @todo: Implement this eventually.
isc::data::ConstElementPtr answer;
answer = getCfgMgr()->parseConfig(config_set, check_only);;
if (check_only) {
return (isc::config::createAnswer(0, "Configuration check is not supported by D2."));
return (answer);
}
int rcode = 0;
isc::data::ConstElementPtr comment;
isc::data::ConstElementPtr answer = getCfgMgr()->parseConfig(config_set);;
comment = isc::config::parseAnswer(rcode, answer);
if (rcode) {
......
......@@ -2,7 +2,7 @@
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
[<!ENTITY mdash "&#8212;">]>
<!--
- Copyright (C) 2013-2016 Internet Systems Consortium, Inc. ("ISC")
- Copyright (C) 2013-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
......@@ -52,8 +52,8 @@
<arg><option>-V</option></arg>
<arg><option>-W</option></arg>
<arg><option>-d</option></arg>
<!-- not yet <arg><option>-t</option></arg> -->
<arg><option>-c</option></arg>
<arg><option>-c<replaceable class="parameter">config-file</replaceable></option></arg>
<arg><option>-t<replaceable class="parameter">config-file</replaceable></option></arg>
</cmdsynopsis>
</refsynopsisdiv>
......@@ -105,21 +105,21 @@
</para></listitem>
</varlistentry>
<!-- not yet
<varlistentry>
<term><option>-t</option></term>
<term><option>-c</option></term>
<listitem><para>
Check the syntax of the configuration file and report the first
error if any.
Configuration file including the configuration for DHCP-DDNS server.
It may also contain configuration entries for other Kea services.
</para></listitem>
</varlistentry>
-->
<varlistentry>
<term><option>-c</option></term>
<term><option>-t</option></term>
<listitem><para>
Configuration file including the configuration for DHCP-DDNS server.
It may also contain configuration entries for other Kea services.
Check the syntax of the configuration file and report the
first error if any. Note that not all parameters are
completely checked, in particular, service socket is
not opened.
</para></listitem>
</varlistentry>
......
// Copyright (C) 2013-2016 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2013-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
......@@ -34,9 +34,18 @@ int main(int argc, char* argv[]) {
// 'false' value disables test mode.
controller->launch(argc, argv, false);
} catch (const VersionMessage& ex) {
std::cout << ex.what() << std::endl;
std::string msg(ex.what());
if (!msg.empty()) {
std::cout << msg << std::endl;
}
} catch (const InvalidUsage& ex) {
std::string msg(ex.what());
if (!msg.empty()) {
std::cerr << msg << std::endl;
}
ret = EXIT_FAILURE;
} catch (const isc::Exception& ex) {
std::cerr << "Service failed:" << ex.what() << std::endl;
std::cerr << "Service failed: " << ex.what() << std::endl;
ret = EXIT_FAILURE;
}
......
......@@ -168,7 +168,8 @@ public:
// The JSON parsed ok and we've added the defaults, pass the config
// into the Element parser and check for the expected outcome.
data::ConstElementPtr answer = cfg_mgr_->parseConfig(config_set_);
data::ConstElementPtr answer;
answer = cfg_mgr_->parseConfig(config_set_, false);
// Extract the result and error text from the answer.
int rcode = 0;
......@@ -601,7 +602,7 @@ TEST_F(D2CfgMgrTest, fullConfig) {
// Verify that parsing the exact same configuration a second time
// does not cause a duplicate value errors.
answer_ = cfg_mgr_->parseConfig(config_set_);
answer_ = cfg_mgr_->parseConfig(config_set_, false);
ASSERT_TRUE(checkAnswer(0));
}
......
......@@ -177,14 +177,24 @@ TEST_F(D2ControllerTest, configUpdateTests) {
isc::data::Element::fromJSON(valid_d2_config);
// Verify that given a valid config we get a successful update result.
answer = updateConfig(config_set);
answer = updateConfig(config_set, false);
isc::config::parseAnswer(rcode, answer);
EXPECT_EQ(0, rcode);
// Verify that given a valid config we get a successful check result.
answer = updateConfig(config_set, true);
isc::config::parseAnswer(rcode, answer);
EXPECT_EQ(0, rcode);
// Use an invalid configuration to verify parsing error return.
std::string config = "{ \"bogus\": 1000 } ";
config_set = isc::data::Element::fromJSON(config);
answer = updateConfig(config_set);
answer = updateConfig(config_set, false);
isc::config::parseAnswer(rcode, answer);
EXPECT_EQ(1, rcode);
// Use an invalid configuration to verify checking error return.
answer = updateConfig(config_set, true);
isc::config::parseAnswer(rcode, answer);
EXPECT_EQ(1, rcode);
}
......
......@@ -152,7 +152,7 @@ public:
// try DHCPDDNS configure
ConstElementPtr status;
try {
status = srv_->parseConfig(d2);
status = srv_->parseConfig(d2, false);
} catch (const std::exception& ex) {
ADD_FAILURE() << "configure for " << operation
<< " failed with " << ex.what()
......
......@@ -122,7 +122,8 @@ DCfgMgrBase::setContext(DCfgContextBasePtr& context) {
}
isc::data::ConstElementPtr
DCfgMgrBase::parseConfig(isc::data::ConstElementPtr config_set) {
DCfgMgrBase::parseConfig(isc::data::ConstElementPtr config_set,
bool check_only) {
LOG_DEBUG(dctl_logger, DBGLVL_COMMAND,
DCTL_CONFIG_START).arg(config_set->str());
......@@ -245,9 +246,15 @@ DCfgMgrBase::parseConfig(isc::data::ConstElementPtr config_set) {
}
// Everything was fine. Configuration set processed successfully.
LOG_INFO(dctl_logger, DCTL_CONFIG_COMPLETE).arg(getConfigSummary(0));
answer = isc::config::createAnswer(0, "Configuration committed.");
if (!check_only) {
LOG_INFO(dctl_logger, DCTL_CONFIG_COMPLETE).arg(getConfigSummary(0));
answer = isc::config::createAnswer(0, "Configuration committed.");
} else {
answer = isc::config::createAnswer(0, "Configuration seems sane.");
LOG_INFO(dctl_logger, DCTL_CONFIG_CHECK_COMPLETE)
.arg(getConfigSummary(0))
.arg(config::answerToText(answer));
}
} catch (const std::exception& ex) {
LOG_ERROR(dctl_logger, DCTL_PARSER_FAIL).arg(ex.what());
answer = isc::config::createAnswer(1, ex.what());
......@@ -257,6 +264,12 @@ DCfgMgrBase::parseConfig(isc::data::ConstElementPtr config_set) {
return (answer);
}
if (check_only) {
// If this is a configuration check only, then don't actually apply
// the configuration and reverse to the previous one.
context_ = original_context;
}
return (answer);
}
......
......@@ -246,7 +246,7 @@ typedef std::vector<std::string> ElementIdList;
/// update context with parsed results
/// break on error
///
/// if an error occurred
/// if an error occurred or this is only a check
/// restore configuration context from backup
/// @endcode
///
......@@ -281,7 +281,7 @@ typedef std::vector<std::string> ElementIdList;
/// 1. implementation calls simpleParseConfig from its configure method.
/// 2. simpleParseConfig makes a configuration context
/// 3. parse method from the derived class is called
/// 4. if the configuration was unsuccessful of this is only a check, the
/// 4. if the configuration was unsuccessful or this is only a check, the
/// old context is reinstantiated. If not, the configuration is kept.
///
/// See @ref isc::agent::CtrlAgentCfgMgr and @ref isc::agent::CtrlAgentProcess
......@@ -303,12 +303,14 @@ public:
/// the parsing as described in the class brief.
///
/// @param config_set is a set of configuration elements to be parsed.
/// @param check_only true if the config is to be checked only, but not applied
///
/// @return an Element that contains the results of configuration composed
/// of an integer status value (0 means successful, non-zero means failure),
/// and a string explanation of the outcome.
isc::data::ConstElementPtr parseConfig(isc::data::ConstElementPtr
config_set);
isc::data::ConstElementPtr
parseConfig(isc::data::ConstElementPtr config_set,
bool check_only = false);
/// @brief Acts as the receiver of new configurations.
......
......@@ -68,11 +68,67 @@ DControllerBase::launch(int argc, char* argv[], const bool test_mode) {
parseArgs(argc, argv);
} catch (const InvalidUsage& ex) {
usage(ex.what());
throw; // rethrow it
// rethrow it with an empty message
isc_throw(InvalidUsage, "");
}
setProcName(bin_name_);
if (!test_mode && check_only_) {
try {
// We need to initialize logging, in case any error
// messages are to be printed.
// This is just a test, so we don't care about lockfile.
setenv("KEA_LOCKFILE_DIR", "none", 0);
isc::dhcp::CfgMgr::instance().setDefaultLoggerName(bin_name_);
isc::dhcp::CfgMgr::instance().setVerbose(verbose_);
Daemon::loggerInit(bin_name_.c_str(), verbose_);
// Check the syntax first.
std::string config_file = getConfigFile();
if (config_file.empty()) {
// Basic sanity check: file name must not be empty.
isc_throw(InvalidUsage,
"JSON configuration file not specified");
}
isc::data::ConstElementPtr whole_config = parseFile(config_file);
if (!whole_config) {
// No fallback to fromJSONFile
isc_throw(InvalidUsage, "No configuration found");
}
if (verbose_) {
std::cerr << "Syntax check OK" << std::endl;
}
// Check the logic next.
isc::data::ConstElementPtr module_config;
module_config = whole_config->get(getAppName());
if (!module_config) {
isc_throw(InvalidUsage, "Config file " << config_file <<
" does not include '" << getAppName() << "' entry");
}
// Get an application process object.
initProcess();
isc::data::ConstElementPtr answer;
answer = updateConfig(module_config, true);
int rcode = 0;
answer = isc::config::parseAnswer(rcode, answer);
if (rcode != 0) {
isc_throw(InvalidUsage, "Error encountered: "
<< answer->stringValue());
}
} catch (const VersionMessage&) {
throw;
} catch (const InvalidUsage&) {
throw;
} catch (const std::exception& ex) {
isc_throw(InvalidUsage, "Syntax check failed with: " << ex.what());
}
return;
}
// It is important that we set a default logger name because this name
// will be used when the user doesn't provide the logging configuration
// in the Kea configuration file.
......@@ -150,12 +206,12 @@ void
DControllerBase::parseArgs(int argc, char* argv[])
{
// Iterate over the given command line options. If its a stock option
// ("s" or "v") handle it here. If its a valid custom option, then
// ("c" or "d") handle it here. If its a valid custom option, then
// invoke customOption.
int ch;
opterr = 0;
optind = 1;
std::string opts("dvVWc:" + getCustomOpts());
std::string opts("dvVWc:t:" + getCustomOpts());
while ((ch = getopt(argc, argv, opts.c_str())) != -1) {
switch (ch) {
case 'd':
......@@ -182,12 +238,17 @@ DControllerBase::parseArgs(int argc, char* argv[])
break;
case 'c':
case 't':
// config file name
if (optarg == NULL) {
isc_throw(InvalidUsage, "configuration file name missing");
}
setConfigFile(optarg);
if (ch == 't') {
check_only_ = true;
}
break;
case '?': {
......@@ -290,7 +351,7 @@ DControllerBase::configFromFile() {
getAppName() << "' entry.");
}
answer = updateConfig(module_config);
answer = updateConfig(module_config, false);
int rcode = 0;
isc::config::parseAnswer(rcode, answer);
if (!rcode) {
......@@ -329,8 +390,9 @@ DControllerBase::runProcess() {
// Instance method for handling new config
isc::data::ConstElementPtr
DControllerBase::updateConfig(isc::data::ConstElementPtr new_config) {
return (process_->configure(new_config, false));
DControllerBase::updateConfig(isc::data::ConstElementPtr new_config,
bool check_only) {
return (process_->configure(new_config, check_only));
}
......
......@@ -24,6 +24,7 @@ namespace isc {
namespace process {
/// @brief Exception thrown when the command line is invalid.
/// Can be used to transmit negative messages too.
class InvalidUsage : public isc::Exception {
public:
InvalidUsage(const char* file, size_t line, const char* what) :
......@@ -34,7 +35,7 @@ public:
/// Since command line argument parsing is done as part of
/// DControllerBase::launch(), it uses this exception to propagate
/// version information up to main(), when command line argument
/// -v or -V is given.
/// -v, -V or -W is given. Can be used to transmit positive messages too.
class VersionMessage : public isc::Exception {
public:
VersionMessage(const char* file, size_t line, const char* what) :
......@@ -128,7 +129,7 @@ public:
/// arguments.
///
/// This function can be run in "test mode". It prevents initialization
/// of D2 module logger. This is used in unit tests which initialize logger
/// of module logger. This is used in unit tests which initialize logger
/// in their main function. Such a logger uses environmental variables to
/// control severity, verbosity etc.
///
......@@ -154,12 +155,14 @@ public:
/// configuration and then invoke the application process' configure method.
///
/// @param new_config is the new configuration
/// @param check_only false for normal configuration, true when verifying only
///
/// @return returns an Element that contains the results of configuration
/// update composed of an integer status value (0 means successful,
/// non-zero means failure), and a string explanation of the outcome.
virtual isc::data::ConstElementPtr updateConfig(isc::data::ConstElementPtr
new_config);
virtual isc::data::ConstElementPtr
updateConfig(isc::data::ConstElementPtr new_config,
bool check_only = false);
/// @brief Reconfigures the process from a configuration file
///
......@@ -223,10 +226,10 @@ public:
/// @return an Element that contains the results of command composed
/// of an integer status value and a string explanation of the outcome.
/// The status value is one of the following:
/// D2::COMMAND_SUCCESS - Command executed successfully
/// D2::COMMAND_ERROR - Command is valid but suffered an operational
/// COMMAND_SUCCESS - Command executed successfully
/// COMMAND_ERROR - Command is valid but suffered an operational
/// failure.
/// D2::COMMAND_INVALID - Command is not recognized as valid be either
/// COMMAND_INVALID - Command is not recognized as valid be either
/// the controller or the application process.
virtual isc::data::ConstElementPtr executeCommand(const std::string&
command,
......@@ -283,10 +286,10 @@ protected:
/// @return an Element that contains the results of command composed
/// of an integer status value and a string explanation of the outcome.
/// The status value is one of the following:
/// D2::COMMAND_SUCCESS - Command executed successfully
/// D2::COMMAND_ERROR - Command is valid but suffered an operational
/// COMMAND_SUCCESS - Command executed successfully
/// COMMAND_ERROR - Command is valid but suffered an operational
/// failure.
/// D2::COMMAND_INVALID - Command is not recognized as a valid custom
/// COMMAND_INVALID - Command is not recognized as a valid custom
/// controller command.
virtual isc::data::ConstElementPtr customControllerCommand(
const std::string& command, isc::data::ConstElementPtr args);
......@@ -302,7 +305,7 @@ protected:
/// @brief Virtual method which returns a string containing the option
/// letters for any custom command line options supported by the derivation.
/// These are added to the stock options of "c" and "v" during command
/// These are added to the stock options of "c", "d", ..., during command
/// line interpretation.
///
/// @return returns a string containing the custom option letters.
......@@ -342,6 +345,20 @@ protected:
verbose_ = value;
}
/// @brief Supplies whether or not check only mode is enabled.
///
/// @return returns true if check only is enabled.
bool isCheckOnly() const {
return (check_only_);
}
/// @brief Method for enabling or disabling check only mode.
///
/// @param value is the new value to assign the flag.
void setCheckOnly(bool value) {
check_only_ = value;
}
/// @brief Getter for fetching the controller's IOService
///
/// @return returns a pointer reference to the IOService.
......@@ -385,14 +402,15 @@ protected:
/// list of options with those returned by getCustomOpts(), and uses
/// cstdlib's getopt to loop through the command line.
/// It handles stock options directly, and passes any custom options into
/// the customOption method. Currently there are only two stock options
/// -c for specifying the configuration file, and -v for verbose logging.
/// the customOption method. Currently there are only some stock options
/// -c/t for specifying the configuration file, -d for verbose logging,
/// and -v/V/W for version reports.
///
/// @param argc is the number of command line arguments supplied
/// @param argv is the array of string (char *) command line arguments
///
/// @throw InvalidUsage when there are usage errors.
/// @throw VersionMessage if the -v or -V arguments is given.
/// @throw VersionMessage if the -v, -V or -W arguments is given.
void parseArgs(int argc, char* argv[]);
......@@ -536,6 +554,9 @@ private:
/// @brief Indicates if the verbose logging mode is enabled.
bool verbose_;
/// @brief Indicates if the check only mode is enabled.
bool check_only_;
/// @brief The absolute file name of the JSON spec file.
std::string spec_file_name_;
......
......@@ -115,13 +115,22 @@ TEST_F(DStubCfgMgrTest, basicParseTest) {
ASSERT_TRUE(fromJSON(config));
// Verify that we can parse a simple configuration.
answer_ = cfg_mgr_->parseConfig(config_set_);
answer_ = cfg_mgr_->parseConfig(config_set_, false);
EXPECT_TRUE(checkAnswer(0));
// Verify that we can check a simple configuration.
answer_ = cfg_mgr_->parseConfig(config_set_, true);
EXPECT_TRUE(checkAnswer(0));
// Verify that an unknown element error is caught and returns a failed
// parse result.
SimFailure::set(SimFailure::ftElementUnknown);
answer_ = cfg_mgr_->parseConfig(config_set_);
answer_ = cfg_mgr_->parseConfig(config_set_, false);
EXPECT_TRUE(checkAnswer(1));
// Verify that an error is caught too when the config is checked for.
SimFailure::set(SimFailure::ftElementUnknown);
answer_ = cfg_mgr_->parseConfig(config_set_, true);
EXPECT_TRUE(checkAnswer(1));
}
......@@ -181,7 +190,7 @@ TEST_F(DStubCfgMgrTest, parseOrderTest) {
EXPECT_EQ(0, cfg_mgr_->getParseOrder().size());
// Parse the configuration, verify it parses without error.
answer_ = cfg_mgr_->parseConfig(config_set_);
answer_ = cfg_mgr_->parseConfig(config_set_, false);
EXPECT_TRUE(checkAnswer(0));
// Verify that the parsed order matches what we expected.
......@@ -197,7 +206,7 @@ TEST_F(DStubCfgMgrTest, parseOrderTest) {
EXPECT_EQ(1, cfg_mgr_->getParseOrder().size());
// Verify the configuration fails.
answer_ = cfg_mgr_->parseConfig(config_set_);
answer_ = cfg_mgr_->parseConfig(config_set_, false);
EXPECT_TRUE(checkAnswer(1));
// Verify that the configuration parses correctly, when the parse order
......@@ -212,7 +221,7 @@ TEST_F(DStubCfgMgrTest, parseOrderTest) {
cfg_mgr_->parsed_order_.clear();
// Verify the configuration parses without error.
answer_ = cfg_mgr_->parseConfig(config_set_);
answer_ = cfg_mgr_->parseConfig(config_set_, false);
EXPECT_TRUE(checkAnswer(0));
// Build expected order
......@@ -238,7 +247,7 @@ TEST_F(DStubCfgMgrTest, parseOrderTest) {
EXPECT_EQ(4, cfg_mgr_->getParseOrder().size());
// Verify the configuration fails.
answer_ = cfg_mgr_->parseConfig(config_set_);
answer_ = cfg_mgr_->parseConfig(config_set_, false);
EXPECT_TRUE(checkAnswer(1));
}
......@@ -262,7 +271,7 @@ TEST_F(DStubCfgMgrTest, simpleTypesTest) {
ASSERT_TRUE(fromJSON(config));
// Verify that the configuration parses without error.
answer_ = cfg_mgr_->parseConfig(config_set_);
answer_ = cfg_mgr_->parseConfig(config_set_, false);
ASSERT_TRUE(checkAnswer(0));
DStubContextPtr context = getStubContext();
ASSERT_TRUE(context);
......@@ -301,7 +310,7 @@ TEST_F(DStubCfgMgrTest, simpleTypesTest) {
ASSERT_TRUE(fromJSON(config2));
// Verify that the configuration parses without error.
answer_ = cfg_mgr_->parseConfig(config_set_);
answer_ = cfg_mgr_->parseConfig(config_set_, false);
EXPECT_TRUE(checkAnswer(0));
context = getStubContext();
ASSERT_TRUE(context);
......@@ -352,7 +361,7 @@ TEST_F(DStubCfgMgrTest, rollBackTest) {
ASSERT_TRUE(fromJSON(config));
// Verify that the configuration parses without error.
answer_ = cfg_mgr_->parseConfig(config_set_);
answer_ = cfg_mgr_->parseConfig(config_set_, false);
EXPECT_TRUE(checkAnswer(0));
DStubContextPtr context = getStubContext();
ASSERT_TRUE(context);
......@@ -389,7 +398,7 @@ TEST_F(DStubCfgMgrTest, rollBackTest) {
// Force a failure on the last element
SimFailure::set(SimFailure::ftElementUnknown);
answer_ = cfg_mgr_->parseConfig(config_set_);
answer_ = cfg_mgr_->parseConfig(config_set_, false);
EXPECT_TRUE(checkAnswer(1));
context = getStubContext();
ASSERT_TRUE(context);
......@@ -414,6 +423,76 @@ TEST_F(DStubCfgMgrTest, rollBackTest) {
EXPECT_TRUE(object);
}
/// @brief Tests that the configuration context is preserved during
/// check only parsing.
TEST_F(DStubCfgMgrTest, checkOnly) {
// Create a configuration with all of the parameters.
string config = "{ \"bool_test\": true , "
" \"uint32_test\": 77 , "
" \"string_test\": \"hmmm chewy\" , "
" \"map_test\" : {} , "
" \"list_test\": [] }";
ASSERT_TRUE(fromJSON(config));
// Verify that the configuration parses without error.
answer_ = cfg_mgr_->parseConfig(config_set_, false);
EXPECT_TRUE(checkAnswer(0));
DStubContextPtr context = getStubContext();
ASSERT_TRUE(context);
// Verify that all of parameters have the expected values.
bool actual_bool = false;
EXPECT_NO_THROW(context->getParam("bool_test", actual_bool));
EXPECT_EQ(true, actual_bool);
uint32_t actual_uint32 = 0;
EXPECT_NO_THROW(context->getParam("uint32_test", actual_uint32));
EXPECT_EQ(77, actual_uint32);
std::string actual_string = "";
EXPECT_NO_THROW(context->getParam("string_test", actual_string));
EXPECT_EQ("hmmm chewy", actual_string);
isc::data::ConstElementPtr object;
EXPECT_NO_THROW(context->getObjectParam("map_test", object));
EXPECT_TRUE(object);
EXPECT_NO_THROW(context->getObjectParam("list_test", object));
EXPECT_TRUE(object);
// Create a configuration which "updates" all of the parameter values.
string config2 = "{ \"bool_test\": false , "
" \"uint32_test\": 88 , "
" \"string_test\": \"ewww yuk!\" , "
" \"map_test2\" : {} , "
" \"list_test2\": [] }";
ASSERT_TRUE(fromJSON(config2));
answer_ = cfg_mgr_->parseConfig(config_set_, true);
EXPECT_TRUE(checkAnswer(0));
context = getStubContext();
ASSERT_TRUE(context);
// Verify that all of parameters have the original values.
actual_bool = false;
EXPECT_NO_THROW(context->getParam("bool_test", actual_bool));
EXPECT_EQ(true, actual_bool);
actual_uint32 = 0;
EXPECT_NO_THROW(context->getParam("uint32_test", actual_uint32));
EXPECT_EQ(77, actual_uint32);
actual_string = "";
EXPECT_NO_THROW(context->getParam("string_test", actual_string));