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
Sebastian Schrader
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) {
expected = current;