Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ISC Open Source Projects
Kea
Commits
d04f9047
Commit
d04f9047
authored
Mar 08, 2017
by
Francis Dupont
Committed by
Tomek Mrugalski
Mar 08, 2017
Browse files
[fdunparse2] Added missing files
parent
f7a742bd
Changes
8
Expand all
Hide whitespace changes
Inline
Side-by-side
src/bin/agent/tests/get_config_unittest.cc
0 → 100644
View file @
d04f9047
// 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/.
#include <config.h>
#include <cc/data.h>
#include <cc/command_interpreter.h>
#include <process/testutils/d_test_stubs.h>
#include <agent/ca_cfg_mgr.h>
#include <agent/parser_context.h>
#include <boost/scoped_ptr.hpp>
#include <gtest/gtest.h>
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include "test_data_files_config.h"
#include "test_libraries.h"
using
namespace
isc
::
agent
;
using
namespace
isc
::
config
;
using
namespace
isc
::
data
;
using
namespace
isc
::
process
;
namespace
{
/// @name How to generate the testdata/get_config.json file
///
/// Define GENERATE_ACTION and recompile. Run ca_unittests on
/// CtrlAgentGetCfgTest redirecting the standard error to a temporary
/// file, e.g. by
/// @code
/// ./ca_unittests --gtest_filter="CtrlAgentGetCfg*" > /dev/null 2> u
/// @endcode
///
/// Update testdata/get_config.json using the temporary file content,
/// (removing head comment and restoring hook library path),
/// recompile without GENERATE_ACTION.
/// @brief the generate action
/// false means do nothing, true means unparse extracted configurations
#ifdef GENERATE_ACTION
const
bool
generate_action
=
true
;
#else
const
bool
generate_action
=
false
;
#endif
/// @brief Read a file into a string
std
::
string
readFile
(
const
std
::
string
&
file_path
)
{
std
::
ifstream
ifs
(
file_path
);
if
(
!
ifs
.
is_open
())
{
ADD_FAILURE
()
<<
"readFile cannot open "
<<
file_path
;
isc_throw
(
isc
::
Unexpected
,
"readFile cannot open "
<<
file_path
);
}
std
::
string
lines
;
std
::
string
line
;
while
(
std
::
getline
(
ifs
,
line
))
{
lines
+=
line
+
"
\n
"
;
}
ifs
.
close
();
return
(
lines
);
}
/// @brief Runs parser in JSON mode
ElementPtr
parseJSON
(
const
std
::
string
&
in
,
bool
verbose
=
false
)
{
try
{
ParserContext
ctx
;
return
(
ctx
.
parseString
(
in
,
ParserContext
::
PARSER_JSON
));
}
catch
(
const
std
::
exception
&
ex
)
{
if
(
verbose
)
{
std
::
cout
<<
"EXCEPTION: "
<<
ex
.
what
()
<<
std
::
endl
;
}
throw
;
}
}
/// @brief Runs parser in AGENT mode
ElementPtr
parseAGENT
(
const
std
::
string
&
in
,
bool
verbose
=
false
)
{
try
{
ParserContext
ctx
;
return
(
ctx
.
parseString
(
in
,
ParserContext
::
PARSER_AGENT
));
}
catch
(
const
std
::
exception
&
ex
)
{
if
(
verbose
)
{
std
::
cout
<<
"EXCEPTION: "
<<
ex
.
what
()
<<
std
::
endl
;
}
throw
;
}
}
/// @brief Replace the library path
void
pathReplacer
(
ConstElementPtr
ca_cfg
)
{
ConstElementPtr
hooks_libs
=
ca_cfg
->
get
(
"hooks-libraries"
);
if
(
!
hooks_libs
||
hooks_libs
->
empty
())
{
return
;
}
ElementPtr
first_lib
=
hooks_libs
->
getNonConst
(
0
);
std
::
string
lib_path
(
BASIC_CALLOUT_LIBRARY
);
first_lib
->
set
(
"library"
,
Element
::
create
(
lib_path
));
}
/// @brief Almost regular agent CfgMgr with internal parse method exposed.
class
NakedAgentCfgMgr
:
public
CtrlAgentCfgMgr
{
public:
using
CtrlAgentCfgMgr
::
parse
;
};
}
/// Test fixture class
class
CtrlAgentGetCfgTest
:
public
ConfigParseTest
{
public:
CtrlAgentGetCfgTest
()
:
rcode_
(
-
1
)
{
srv_
.
reset
(
new
NakedAgentCfgMgr
());
// Create fresh context.
resetConfiguration
();
}
~
CtrlAgentGetCfgTest
()
{
resetConfiguration
();
}
/// @brief Parse and Execute configuration
///
/// Parses a configuration and executes a configuration of the server.
/// If the operation fails, the current test will register a failure.
///
/// @param config Configuration to parse
/// @param operation Operation being performed. In the case of an error,
/// the error text will include the string "unable to <operation>.".
///
/// @return true if the configuration succeeded, false if not.
bool
executeConfiguration
(
const
std
::
string
&
config
,
const
char
*
operation
)
{
// try JSON parser
ConstElementPtr
json
;
try
{
json
=
parseJSON
(
config
,
true
);
}
catch
(
const
std
::
exception
&
ex
)
{
ADD_FAILURE
()
<<
"invalid JSON for "
<<
operation
<<
" failed with "
<<
ex
.
what
()
<<
" on
\n
"
<<
config
<<
"
\n
"
;
return
(
false
);
}
// try AGENT parser
try
{
json
=
parseAGENT
(
config
,
true
);
}
catch
(...)
{
ADD_FAILURE
()
<<
"parsing failed for "
<<
operation
<<
" on
\n
"
<<
prettyPrint
(
json
)
<<
"
\n
"
;
return
(
false
);
}
// get Control-agent element
ConstElementPtr
ca
=
json
->
get
(
"Control-agent"
);
if
(
!
ca
)
{
ADD_FAILURE
()
<<
"cannot get Control-agent for "
<<
operation
<<
" on
\n
"
<<
prettyPrint
(
json
)
<<
"
\n
"
;
return
(
false
);
}
// update hooks-libraries
pathReplacer
(
ca
);
// try AGENT configure
ConstElementPtr
status
;
try
{
status
=
srv_
->
parse
(
ca
,
true
);
}
catch
(
const
std
::
exception
&
ex
)
{
ADD_FAILURE
()
<<
"configure for "
<<
operation
<<
" failed with "
<<
ex
.
what
()
<<
" on
\n
"
<<
prettyPrint
(
json
)
<<
"
\n
"
;
return
(
false
);
}
// The status object must not be NULL
if
(
!
status
)
{
ADD_FAILURE
()
<<
"configure for "
<<
operation
<<
" returned null on
\n
"
<<
prettyPrint
(
json
)
<<
"
\n
"
;
return
(
false
);
}
// Returned value should be 0 (configuration success)
comment_
=
parseAnswer
(
rcode_
,
status
);
if
(
rcode_
!=
0
)
{
string
reason
=
""
;
if
(
comment_
)
{
reason
=
string
(
" ("
)
+
comment_
->
stringValue
()
+
string
(
")"
);
}
ADD_FAILURE
()
<<
"configure for "
<<
operation
<<
" returned error code "
<<
rcode_
<<
reason
<<
" on
\n
"
<<
prettyPrint
(
json
)
<<
"
\n
"
;
return
(
false
);
}
return
(
true
);
}
/// @brief Reset configuration database.
///
/// This function resets configuration data base by
/// removing control sockets and hooks. Reset must
/// be performed after each test to make sure that
/// contents of the database do not affect result of
/// subsequent tests.
void
resetConfiguration
()
{
string
config
=
"{
\"
Control-agent
\"
: {"
"
\"
http-host
\"
:
\"\"
,"
"
\"
http-port
\"
: 0 } }"
;
EXPECT_TRUE
(
executeConfiguration
(
config
,
"reset config"
));
}
boost
::
scoped_ptr
<
NakedAgentCfgMgr
>
srv_
;
///< CA server under test
int
rcode_
;
///< Return code from element parsing
ConstElementPtr
comment_
;
///< Reason for parse fail
};
/// Test a configuration
TEST_F
(
CtrlAgentGetCfgTest
,
simple
)
{
// get the simple configuration
std
::
string
simple_file
=
string
(
CFG_EXAMPLES
)
+
"/"
+
"simple.json"
;
std
::
string
config
;
ASSERT_NO_THROW
(
config
=
readFile
(
simple_file
));
// get the expected configuration
std
::
string
expected_file
=
std
::
string
(
CA_TEST_DATA_DIR
)
+
"/"
+
"get_config.json"
;
std
::
string
expected
;
ASSERT_NO_THROW
(
expected
=
readFile
(
expected_file
));
// execute the sample configuration
ASSERT_TRUE
(
executeConfiguration
(
config
,
"simple config"
));
// unparse it
CtrlAgentCfgContextPtr
context
=
srv_
->
getCtrlAgentCfgContext
();
ConstElementPtr
unparsed
;
ASSERT_NO_THROW
(
unparsed
=
context
->
toElement
());
// dump if wanted else check
if
(
generate_action
)
{
std
::
cerr
<<
"// Generated Configuration (remove this line)
\n
"
;
ASSERT_NO_THROW
(
expected
=
prettyPrint
(
unparsed
));
prettyPrint
(
unparsed
,
std
::
cerr
,
0
,
4
);
std
::
cerr
<<
"
\n
"
;
}
else
{
ConstElementPtr
json
;
ASSERT_NO_THROW
(
json
=
parseAGENT
(
expected
,
true
));
ConstElementPtr
ca
;
ASSERT_NO_THROW
(
ca
=
json
->
get
(
"Control-agent"
));
ASSERT_TRUE
(
ca
);
pathReplacer
(
ca
);
EXPECT_TRUE
(
isEquivalent
(
unparsed
,
json
));
std
::
string
current
=
prettyPrint
(
unparsed
,
0
,
4
);
std
::
string
expected2
=
prettyPrint
(
json
,
0
,
4
);
EXPECT_EQ
(
expected2
,
current
);
if
(
expected2
!=
current
)
{
expected
=
current
+
"
\n
"
;
}
}
// execute the control agent configuration
EXPECT_TRUE
(
executeConfiguration
(
expected
,
"unparsed config"
));
// is it a fixed point?
CtrlAgentCfgContextPtr
context2
=
srv_
->
getCtrlAgentCfgContext
();
ConstElementPtr
unparsed2
;
ASSERT_NO_THROW
(
unparsed2
=
context2
->
toElement
());
ASSERT_TRUE
(
unparsed2
);
EXPECT_TRUE
(
isEquivalent
(
unparsed
,
unparsed2
));
}
src/bin/agent/tests/test_data_files_config.h.in
0 → 100644
View file @
d04f9047
// 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/.
/// @brief Path to agent source dir
#define CA_SRC_DIR "@abs_top_srcdir@/src/bin/agent"
#define CA_TEST_DATA_DIR "@abs_top_srcdir@/src/bin/agent/tests/testdata"
src/bin/dhcp6/tests/get_config_unittest.cc
0 → 100644
View file @
d04f9047
This diff is collapsed.
Click to expand it.
src/bin/dhcp6/tests/get_config_unittest.cc.skel
0 → 100644
View file @
d04f9047
// 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/.
#include <config.h>
#include <cc/command_interpreter.h>
#include <cc/data.h>
#include <cc/simple_parser.h>
#include <cc/cfg_to_element.h>
#include <dhcp/tests/iface_mgr_test_config.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcp6/tests/dhcp6_test_utils.h>
#include <dhcp6/tests/get_config_unittest.h>
#include <dhcp6/dhcp6_srv.h>
#include <dhcp6/json_config_parser.h>
#include <dhcp6/simple_parser6.h>
#include <boost/algorithm/string.hpp>
#include <gtest/gtest.h>
#include <iostream>
#include <string>
#include <sstream>
#include <list>
using namespace isc::config;
using namespace isc::data;
using namespace isc::dhcp;
using namespace isc::dhcp::test;
namespace {
/// @name How to fill configurations
///
/// Copy get_config_unittest.cc.skel into get_config_unittest.cc
///
/// For the extracted configurations define the EXTRACT_CONFIG and
/// recompile this file. Run dhcp6_unittests on Dhcp6ParserTest
/// redirecting the standard error to a temporary file, e.g. by
/// @code
/// ./dhcp6_unittests --gtest_filter="Dhcp6Parser*" > /dev/null 2> x
/// @endcode
///
/// Update EXTRACTED_CONFIGS with the file content
///
/// When configurations have been extracted the corresponding unparsed
/// configurations must be generated. To do that define GENERATE_ACTION
/// and recompile this file. Run dhcp6_unittests on Dhcp6GetConfigTest
/// redirecting the standard error to a temporary file, e.g. by
/// @code
/// ./dhcp6_unittests --gtest_filter="Dhcp6GetConfig*" > /dev/null 2> u
/// @endcode
///
/// Update UNPARSED_CONFIGS with the file content, recompile this file
/// without EXTRACT_CONFIG and GENERATE_ACTION.
///
/// @note Check for failures at each step!
/// @note The tests of this file do not check if configs returned
/// by @ref isc::dhcp::CfgToElement::ToElement() are complete.
/// This has to be done manually.
///
///@{
/// @brief extracted configurations
const char* EXTRACTED_CONFIGS[] = {
// "to be replaced"
};
/// @brief unparsed configurations
const char* UNPARSED_CONFIGS[] = {
// "to be replaced"
};
/// @brief the number of configurations
const size_t max_config_counter = sizeof(EXTRACTED_CONFIGS) / sizeof(char*);
///@}
/// @brief the extraction counter
///
/// < 0 means do not extract, >= 0 means extract on extractConfig() calls
/// and increment
#ifdef EXTRACT_CONFIG
int extract_count = 0;
#else
int extract_count = -1;
#endif
/// @brief the generate action
/// false means do nothing, true means unparse extracted configurations
#ifdef GENERATE_ACTION
const bool generate_action = true;
#else
const bool generate_action = false;
static_assert(max_config_counter == sizeof(UNPARSED_CONFIGS) / sizeof(char*),
"unparsed configurations must be generated");
#endif
/// @brief format and output a configuration
void
outputFormatted(const std::string& config) {
// pretty print it
ConstElementPtr json = parseJSON(config);
std::string prettier = prettyPrint(json, 4, 4);
// get it as a line array
std::list<std::string> lines;
boost::split(lines, prettier, boost::is_any_of("\n"));
// add escapes using again JSON
std::list<std::string> escapes;
while (!lines.empty()) {
const std::string& line = lines.front();
ConstElementPtr escaping = Element::create(line + "\n");
escapes.push_back(escaping->str());
lines.pop_front();
}
// output them on std::cerr
while (!escapes.empty()) {
std::cerr << "\n" << escapes.front();
escapes.pop_front();
}
}
};
namespace isc {
namespace dhcp {
namespace test {
/// @ref isc::dhcp::test::extractConfig in the header
void
extractConfig(const std::string& config) {
// skip when disable
if (extract_count < 0) {
return;
}
// mark beginning
if (extract_count == 0) {
// header (note there is no trailer)
std::cerr << "/// put this after const char* EXTRACTED_CONFIGS[] = {\n";
} else {
// end of previous configuration
std::cerr << ",\n";
}
std::cerr << " // CONFIGURATION " << extract_count;
try {
outputFormatted(config);
} catch (...) {
// mark error
std::cerr << "\n//// got an error\n";
}
++extract_count;
}
};
};
};
namespace {
/// Test fixture class (code from Dhcp6ParserTest)
class Dhcp6GetConfigTest : public ::testing::TestWithParam<size_t> {
public:
Dhcp6GetConfigTest() : rcode_(-1), srv_(0) {
// srv_(0) means to not open any sockets. We don't want to
// deal with sockets here, just check if configuration handling
// is sane.
// Reset configuration for each test.
resetConfiguration();
}
~Dhcp6GetConfigTest() {
// Reset configuration database after each test.
resetConfiguration();
};
/// @brief Parse and Execute configuration
///
/// Parses a configuration and executes a configuration of the server.
/// If the operation fails, the current test will register a failure.
///
/// @param config Configuration to parse
/// @param operation Operation being performed. In the case of an error,
/// the error text will include the string "unable to <operation>.".
///
/// @return true if the configuration succeeded, false if not.
bool
executeConfiguration(const std::string& config, const char* operation) {
// clear config manager
CfgMgr::instance().clear();
// enable fake network interfaces
IfaceMgrTestConfig test_config(true);
// try JSON parser
ConstElementPtr json;
try {
json = parseJSON(config);
} catch (const std::exception& ex) {
ADD_FAILURE() << "invalid JSON for " << operation
<< " failed with " << ex.what()
<< " on\n" << config << "\n";
return (false);
}
// try DHCP6 parser
try {
json = parseDHCP6(config, true);
} catch (...) {
ADD_FAILURE() << "parsing failed for " << operation
<< " on\n" << prettyPrint(json) << "\n";
return (false);
}
// try DHCP6 configure
ConstElementPtr status;
try {
status = configureDhcp6Server(srv_, json);
} catch (const std::exception& ex) {
ADD_FAILURE() << "configure for " << operation
<< " failed with " << ex.what()
<< " on\n" << prettyPrint(json) << "\n";
return (false);
}
// The status object must not be NULL
if (!status) {
ADD_FAILURE() << "configure for " << operation
<< " returned null on\n"
<< prettyPrint(json) << "\n";
return (false);
}
// Returned value should be 0 (configuration success)
comment_ = parseAnswer(rcode_, status);
if (rcode_ != 0) {
string reason = "";
if (comment_) {
reason = string(" (") + comment_->stringValue() + string(")");
}
ADD_FAILURE() << "configure for " << operation
<< " returned error code "
<< rcode_ << reason << " on\n"
<< prettyPrint(json) << "\n";
return (false);
}
return (true);
}
/// @brief Reset configuration database.
///
/// This function resets configuration data base by
/// removing all subnets and option-data. Reset must
/// be performed after each test to make sure that
/// contents of the database do not affect result of
/// subsequent tests.
void resetConfiguration() {
string config = "{"
"\"interfaces-config\": { \"interfaces\": [ \"*\" ] },"
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"valid-lifetime\": 4000, "
"\"subnet6\": [ ], "
"\"dhcp-ddns\": { \"enable-updates\" : false }, "
"\"option-def\": [ ], "
"\"option-data\": [ ] }";
EXPECT_TRUE(executeConfiguration(config, "reset configuration"));
CfgMgr::instance().clear();
CfgMgr::instance().setFamily(AF_INET6);
}
int rcode_; ///< Return code (see @ref isc::config::parseAnswer)
Dhcpv6Srv srv_; ///< Instance of the Dhcp6Srv used during tests
ConstElementPtr comment_; ///< Comment (see @ref isc::config::parseAnswer)
};
/// Test a configuration
TEST_P(Dhcp6GetConfigTest, run) {
// configurations have not been extracted yet
if (max_config_counter == 0) {
return;
}
// get the index of configurations to test
size_t config_counter = GetParam();
// emit unparsed header if wanted
if ((config_counter == 0) && generate_action) {
std::cerr << "///put this after const char* UNPARSED_CONFIGS[] = {\n";
}
// get the extracted configuration
std::string config = EXTRACTED_CONFIGS[config_counter];
std::ostringstream ss;
ss << "extracted config #" << config_counter;
// execute the extracted configuration
ASSERT_TRUE(executeConfiguration(config, ss.str().c_str()));
// unparse it
ConstSrvConfigPtr extracted = CfgMgr::instance().getStagingCfg();
ConstElementPtr unparsed;
ASSERT_NO_THROW(unparsed = extracted->toElement());
ConstElementPtr dhcp;
ASSERT_NO_THROW(dhcp = unparsed->get("Dhcp6"));
ASSERT_TRUE(dhcp);
// dump if wanted else check
std::string expected;
if (generate_action) {
if (config_counter > 0) {
std::cerr << ",\n";
}
std::cerr << " // CONFIGURATION " << config_counter;
ASSERT_NO_THROW(expected = prettyPrint(dhcp));
ASSERT_NO_THROW(outputFormatted(dhcp->str()));
} else {
expected = UNPARSED_CONFIGS[config_counter];
ConstElementPtr json;
ASSERT_NO_THROW(json = parseDHCP6(expected, true));
EXPECT_TRUE(isEquivalent(dhcp, json));
std::string current = prettyPrint(dhcp, 4, 4) + "\n";
EXPECT_EQ(expected, current);
if (expected != current) {