|
@@ -0,0 +1,414 @@
|
|
|
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
|
|
|
+//
|
|
|
+// Permission to use, copy, modify, and/or distribute this software for any
|
|
|
+// purpose with or without fee is hereby granted, provided that the above
|
|
|
+// copyright notice and this permission notice appear in all copies.
|
|
|
+//
|
|
|
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
|
|
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
|
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
|
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
|
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
|
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
|
+// PERFORMANCE OF THIS SOFTWARE.
|
|
|
+
|
|
|
+#include <config/ccsession.h>
|
|
|
+#include <config/module_spec.h>
|
|
|
+#include <dhcpsrv/dhcp_parsers.h>
|
|
|
+#include <d2/d_cfg_mgr.h>
|
|
|
+#include <d_test_stubs.h>
|
|
|
+
|
|
|
+#include <boost/date_time/posix_time/posix_time.hpp>
|
|
|
+#include <gtest/gtest.h>
|
|
|
+
|
|
|
+#include <config.h>
|
|
|
+#include <sstream>
|
|
|
+
|
|
|
+using namespace std;
|
|
|
+using namespace isc;
|
|
|
+using namespace isc::config;
|
|
|
+using namespace isc::d2;
|
|
|
+using namespace boost::posix_time;
|
|
|
+
|
|
|
+namespace {
|
|
|
+
|
|
|
+/// @brief Test Class for verifying that configuration context cannot be null
|
|
|
+/// during construction.
|
|
|
+class DCtorTestCfgMgr : public DCfgMgrBase {
|
|
|
+public:
|
|
|
+ /// @brief Constructor - Note that is passes in an empty configuration
|
|
|
+ /// pointer to the base class constructor.
|
|
|
+ DCtorTestCfgMgr() : DCfgMgrBase(DCfgContextBasePtr()) {
|
|
|
+ }
|
|
|
+
|
|
|
+ /// @brief Destructor
|
|
|
+ virtual ~DCtorTestCfgMgr() {
|
|
|
+ }
|
|
|
+
|
|
|
+ /// @brief Dummy implementation as this method is abstract.
|
|
|
+ virtual isc::dhcp::ParserPtr
|
|
|
+ createConfigParser(const std::string& /* element_id */) {
|
|
|
+ return (isc::dhcp::ParserPtr());
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+/// @brief Test fixture class for testing DCfgMgrBase class.
|
|
|
+/// It maintains an member instance of DStubCfgMgr and provides methods for
|
|
|
+/// converting JSON strings to configuration element sets, checking parse
|
|
|
+/// results, and accessing the configuration context.
|
|
|
+class DStubCfgMgrTest : public ::testing::Test {
|
|
|
+public:
|
|
|
+
|
|
|
+ /// @brief Constructor
|
|
|
+ DStubCfgMgrTest():cfg_mgr_(new DStubCfgMgr) {
|
|
|
+ }
|
|
|
+
|
|
|
+ /// @brief Destructor
|
|
|
+ ~DStubCfgMgrTest() {
|
|
|
+ }
|
|
|
+
|
|
|
+ /// @brief Converts a given JSON string into an Element set and stores the
|
|
|
+ /// result the member variable, config_set_.
|
|
|
+ ///
|
|
|
+ /// @param json_text contains the configuration text in JSON format to
|
|
|
+ /// convert.
|
|
|
+ /// @return returns true if the conversion is successful, false otherwise.
|
|
|
+ bool fromJSON(std::string& json_text) {
|
|
|
+ try {
|
|
|
+ config_set_ = isc::data::Element::fromJSON(json_text);
|
|
|
+ } catch (...) {
|
|
|
+ // This is so we can diagnose parsing mistakes during test
|
|
|
+ // development.
|
|
|
+ std::cerr << "fromJSON failed to parse text" << json_text
|
|
|
+ << std::endl;
|
|
|
+ return (false);
|
|
|
+ }
|
|
|
+
|
|
|
+ return (true);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// @brief Compares the status in the parse result stored in member
|
|
|
+ /// variable answer_ to a given value.
|
|
|
+ ///
|
|
|
+ /// @param should_be is an integer against which to compare the status.
|
|
|
+ ///
|
|
|
+ /// @return returns true if the status value is equal to the given value.
|
|
|
+ bool checkAnswer(int should_be) {
|
|
|
+ int rcode = 0;
|
|
|
+ isc::data::ConstElementPtr comment;
|
|
|
+ comment = isc::config::parseAnswer(rcode, answer_);
|
|
|
+ //std::cout << "checkAnswer rcode:" << rcode << " comment: "
|
|
|
+ // << *comment_ << std::endl;
|
|
|
+ return (rcode == should_be);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// @brief Convenience method which returns a DStubContextPtr to the
|
|
|
+ /// configuration context.
|
|
|
+ ///
|
|
|
+ /// @return returns a DStubContextPtr.
|
|
|
+ DStubContextPtr getStubContext() {
|
|
|
+ return (boost::dynamic_pointer_cast<DStubContext>
|
|
|
+ (cfg_mgr_->getContext()));
|
|
|
+ }
|
|
|
+
|
|
|
+ /// @brief Configuration manager instance.
|
|
|
+ DStubCfgMgrPtr cfg_mgr_;
|
|
|
+
|
|
|
+ /// @brief Configuration set being tested.
|
|
|
+ isc::data::ElementPtr config_set_;
|
|
|
+
|
|
|
+ /// @brief Results of most recent elemnt parsing.
|
|
|
+ isc::data::ConstElementPtr answer_;
|
|
|
+};
|
|
|
+
|
|
|
+///@brief Tests basic construction/destruction of configuration manager.
|
|
|
+/// Verifies that:
|
|
|
+/// 1. Proper construction succeeds.
|
|
|
+/// 2. Configuration context is initialized by construction.
|
|
|
+/// 3. Destruction works properly.
|
|
|
+/// 4. Construction with a null context is not allowed.
|
|
|
+TEST(DCfgMgrBase, construction) {
|
|
|
+ DCfgMgrBase *cfg_mgr = NULL;
|
|
|
+
|
|
|
+ // Verify that configuration manager constructions without error.
|
|
|
+ ASSERT_NO_THROW(cfg_mgr=new DStubCfgMgr());
|
|
|
+
|
|
|
+ // Verify that the context can be retrieved and is not null.
|
|
|
+ DCfgContextBasePtr context = cfg_mgr->getContext();
|
|
|
+ EXPECT_TRUE(context);
|
|
|
+
|
|
|
+ // Verify that the manager can be destructed without error.
|
|
|
+ EXPECT_NO_THROW(delete cfg_mgr);
|
|
|
+
|
|
|
+ // Verify that an attempt to construct a manger with a null context fails.
|
|
|
+ ASSERT_THROW(DCtorTestCfgMgr(), DCfgMgrBaseError);
|
|
|
+}
|
|
|
+
|
|
|
+///@brief Tests fundamental aspects of configuration parsing.
|
|
|
+/// Verifies that:
|
|
|
+/// 1. A correctly formed simple configuration parses without error.
|
|
|
+/// 2. An error building the element is handled.
|
|
|
+/// 3. An error committing the element is handled.
|
|
|
+/// 4. An unknown element error is handled.
|
|
|
+TEST_F(DStubCfgMgrTest, basicParseTest) {
|
|
|
+ // Create a simple configuration.
|
|
|
+ string config = "{ \"test-value\": 1000 } ";
|
|
|
+ ASSERT_TRUE(fromJSON(config));
|
|
|
+
|
|
|
+ // Verify that we can parse a simple configuration.
|
|
|
+ answer_ = cfg_mgr_->parseConfig(config_set_);
|
|
|
+ EXPECT_TRUE(checkAnswer(0));
|
|
|
+
|
|
|
+ // Verify that an error building the element is caught and returns a
|
|
|
+ // failed parse result.
|
|
|
+ SimFailure::set(SimFailure::ftElementBuild);
|
|
|
+ answer_ = cfg_mgr_->parseConfig(config_set_);
|
|
|
+ EXPECT_TRUE(checkAnswer(1));
|
|
|
+
|
|
|
+ // Verify that an error committing the element is caught and returns a
|
|
|
+ // failed parse result.
|
|
|
+ SimFailure::set(SimFailure::ftElementCommit);
|
|
|
+ answer_ = cfg_mgr_->parseConfig(config_set_);
|
|
|
+ EXPECT_TRUE(checkAnswer(1));
|
|
|
+
|
|
|
+ // Verify that an unknown element error is caught and returns a failed
|
|
|
+ // parse result.
|
|
|
+ SimFailure::set(SimFailure::ftElementUnknown);
|
|
|
+ answer_ = cfg_mgr_->parseConfig(config_set_);
|
|
|
+ EXPECT_TRUE(checkAnswer(1));
|
|
|
+}
|
|
|
+
|
|
|
+///@brief Tests ordered and non-ordered element parsing
|
|
|
+/// This test verifies that:
|
|
|
+/// 1. Non-ordered parsing parses elements in the order they are presented
|
|
|
+/// by the configuration set (as-they-come).
|
|
|
+/// 2. A parse order list with too few elements is detected.
|
|
|
+/// 3. Ordered parsing parses the elements in the order specified by the
|
|
|
+/// configuration manager's parse order list.
|
|
|
+TEST_F(DStubCfgMgrTest, parseOrderTest) {
|
|
|
+ // Element ids used for test.
|
|
|
+ std::string charlie("charlie");
|
|
|
+ std::string bravo("bravo");
|
|
|
+ std::string alpha("alpha");
|
|
|
+
|
|
|
+ // Create the test configuration with the elements in "random" order.
|
|
|
+
|
|
|
+ // NOTE that element sets produced by isc::data::Element::fromJSON(),
|
|
|
+ // are in lexical order by element_id. This means that iterating over
|
|
|
+ // such an element set, will present the elements in lexical order. Should
|
|
|
+ // this change, this test will need to be modified accordingly.
|
|
|
+ string config = "{ \"bravo\": 2, "
|
|
|
+ " \"alpha\": 1, "
|
|
|
+ " \"charlie\": 3 } ";
|
|
|
+ ASSERT_TRUE(fromJSON(config));
|
|
|
+
|
|
|
+ // Verify that non-ordered parsing, results in an as-they-come parse order.
|
|
|
+ // Create an expected parse order.
|
|
|
+ // (NOTE that iterating over Element sets produced by fromJSON() will
|
|
|
+ // present the elements in lexical order. Should this change, the expected
|
|
|
+ // order list below would need to be changed accordingly).
|
|
|
+ ElementIdList order_expected;
|
|
|
+ order_expected.push_back(alpha);
|
|
|
+ order_expected.push_back(bravo);
|
|
|
+ order_expected.push_back(charlie);
|
|
|
+
|
|
|
+ // Verify that the manager has an EMPTY parse order list. (Empty list
|
|
|
+ // instructs the manager to parse them as-they-come.)
|
|
|
+ EXPECT_EQ(0, cfg_mgr_->getParseOrder().size());
|
|
|
+
|
|
|
+ // Parse the configuration, verify it parses without error.
|
|
|
+ answer_ = cfg_mgr_->parseConfig(config_set_);
|
|
|
+ EXPECT_TRUE(checkAnswer(0));
|
|
|
+
|
|
|
+ // Verify that the parsed order matches what we expected.
|
|
|
+ EXPECT_TRUE(cfg_mgr_->parsed_order_ == order_expected);
|
|
|
+
|
|
|
+ // Clear the manager's parse order "memory".
|
|
|
+ cfg_mgr_->parsed_order_.clear();
|
|
|
+
|
|
|
+ // Create a parse order list that has too few entries. Verify that
|
|
|
+ // when parsing the test config, it fails.
|
|
|
+ cfg_mgr_->addToParseOrder(charlie);
|
|
|
+ // Verify the parse order list is the size we expect.
|
|
|
+ EXPECT_EQ(1, cfg_mgr_->getParseOrder().size());
|
|
|
+
|
|
|
+ // Verify the configuration fails.
|
|
|
+ answer_ = cfg_mgr_->parseConfig(config_set_);
|
|
|
+ EXPECT_TRUE(checkAnswer(1));
|
|
|
+
|
|
|
+ // Verify that the configuration parses correctly, when the parse order
|
|
|
+ // is correct. Add the needed entries to the parse order
|
|
|
+ cfg_mgr_->addToParseOrder(bravo);
|
|
|
+ cfg_mgr_->addToParseOrder(alpha);
|
|
|
+
|
|
|
+ // Verify the parse order list is the size we expect.
|
|
|
+ EXPECT_EQ(3, cfg_mgr_->getParseOrder().size());
|
|
|
+
|
|
|
+ // Clear the manager's parse order "memory".
|
|
|
+ cfg_mgr_->parsed_order_.clear();
|
|
|
+
|
|
|
+ // Verify the configuration parses without error.
|
|
|
+ answer_ = cfg_mgr_->parseConfig(config_set_);
|
|
|
+ EXPECT_TRUE(checkAnswer(0));
|
|
|
+
|
|
|
+ // Verify that the parsed order is the order we configured.
|
|
|
+ EXPECT_TRUE(cfg_mgr_->getParseOrder() == cfg_mgr_->parsed_order_);
|
|
|
+}
|
|
|
+
|
|
|
+/// @brief Tests that element ids supported by the base class as well as those
|
|
|
+/// added by the derived class function properly.
|
|
|
+/// This test verifies that:
|
|
|
+/// 1. Boolean parameters can be parsed and retrieved.
|
|
|
+/// 2. Uint32 parameters can be parsed and retrieved.
|
|
|
+/// 3. String parameters can be parsed and retrieved.
|
|
|
+/// 4. Derivation-specific parameters can be parsed and retrieved.
|
|
|
+/// 5. Parsing a second configuration, updates the existing context values
|
|
|
+/// correctly.
|
|
|
+TEST_F(DStubCfgMgrTest, simpleTypesTest) {
|
|
|
+ // Fetch a derivation specific pointer to the context.
|
|
|
+ DStubContextPtr context = getStubContext();
|
|
|
+ ASSERT_TRUE(context);
|
|
|
+
|
|
|
+ // Create a configuration with all of the parameters.
|
|
|
+ string config = "{ \"bool_test\": true , "
|
|
|
+ " \"uint32_test\": 77 , "
|
|
|
+ " \"string_test\": \"hmmm chewy\" , "
|
|
|
+ " \"extra_test\": 430 } ";
|
|
|
+ ASSERT_TRUE(fromJSON(config));
|
|
|
+
|
|
|
+ // Verify that the configuration parses without error.
|
|
|
+ answer_ = cfg_mgr_->parseConfig(config_set_);
|
|
|
+ ASSERT_TRUE(checkAnswer(0));
|
|
|
+
|
|
|
+ // Verify that the boolean parameter was parsed correctly by retrieving
|
|
|
+ // its value from the context.
|
|
|
+ bool actual_bool = false;
|
|
|
+ EXPECT_NO_THROW(context->getParam("bool_test", actual_bool));
|
|
|
+ EXPECT_EQ(true, actual_bool);
|
|
|
+
|
|
|
+ // Verify that the uint32 parameter was parsed correctly by retrieving
|
|
|
+ // its value from the context.
|
|
|
+ uint32_t actual_uint32 = 0;
|
|
|
+ EXPECT_NO_THROW(context->getParam("uint32_test", actual_uint32));
|
|
|
+ EXPECT_EQ(77, actual_uint32);
|
|
|
+
|
|
|
+ // Verify that the string parameter was parsed correctly by retrieving
|
|
|
+ // its value from the context.
|
|
|
+ std::string actual_string = "";
|
|
|
+ EXPECT_NO_THROW(context->getParam("string_test", actual_string));
|
|
|
+ EXPECT_EQ("hmmm chewy", actual_string);
|
|
|
+
|
|
|
+ // Verify that the "extra" parameter was parsed correctly by retrieving
|
|
|
+ // its value from the context.
|
|
|
+ uint32_t actual_extra = 0;
|
|
|
+ EXPECT_NO_THROW(context->getExtraParam("extra_test", actual_extra));
|
|
|
+ EXPECT_EQ(430, actual_extra);
|
|
|
+
|
|
|
+ // Create a configuration which "updates" all of the parameter values.
|
|
|
+ string config2 = "{ \"bool_test\": false , "
|
|
|
+ " \"uint32_test\": 88 , "
|
|
|
+ " \"string_test\": \"ewww yuk!\" , "
|
|
|
+ " \"extra_test\": 11 } ";
|
|
|
+ ASSERT_TRUE(fromJSON(config2));
|
|
|
+
|
|
|
+ // Verify that the configuration parses without error.
|
|
|
+ answer_ = cfg_mgr_->parseConfig(config_set_);
|
|
|
+ EXPECT_TRUE(checkAnswer(0));
|
|
|
+
|
|
|
+ // Verify that the boolean parameter was updated correctly by retrieving
|
|
|
+ // its value from the context.
|
|
|
+ actual_bool = true;
|
|
|
+ EXPECT_NO_THROW(context->getParam("bool_test", actual_bool));
|
|
|
+ EXPECT_EQ(false, actual_bool);
|
|
|
+
|
|
|
+ // Verify that the uint32 parameter was updated correctly by retrieving
|
|
|
+ // its value from the context.
|
|
|
+ actual_uint32 = 0;
|
|
|
+ EXPECT_NO_THROW(context->getParam("uint32_test", actual_uint32));
|
|
|
+ EXPECT_EQ(88, actual_uint32);
|
|
|
+
|
|
|
+ // Verify that the string parameter was updated correctly by retrieving
|
|
|
+ // its value from the context.
|
|
|
+ actual_string = "";
|
|
|
+ EXPECT_NO_THROW(context->getParam("string_test", actual_string));
|
|
|
+ EXPECT_EQ("ewww yuk!", actual_string);
|
|
|
+
|
|
|
+ // Verify that the "extra" parameter was updated correctly by retrieving
|
|
|
+ // its value from the context.
|
|
|
+ actual_extra = 0;
|
|
|
+ EXPECT_NO_THROW(context->getExtraParam("extra_test", actual_extra));
|
|
|
+ EXPECT_EQ(11, actual_extra);
|
|
|
+}
|
|
|
+
|
|
|
+/// @brief Tests that the configuration context is preserved after failure
|
|
|
+/// during parsing causes a rollback.
|
|
|
+/// 1. Verifies configuration context rollback.
|
|
|
+TEST_F(DStubCfgMgrTest, rollBackTest) {
|
|
|
+ // Fetch a derivation specific pointer to the context.
|
|
|
+ DStubContextPtr context = getStubContext();
|
|
|
+ ASSERT_TRUE(context);
|
|
|
+
|
|
|
+ // Create a configuration with all of the parameters.
|
|
|
+ string config = "{ \"bool_test\": true , "
|
|
|
+ " \"uint32_test\": 77 , "
|
|
|
+ " \"string_test\": \"hmmm chewy\" , "
|
|
|
+ " \"extra_test\": 430 } ";
|
|
|
+ ASSERT_TRUE(fromJSON(config));
|
|
|
+
|
|
|
+ // Verify that the configuration parses without error.
|
|
|
+ answer_ = cfg_mgr_->parseConfig(config_set_);
|
|
|
+ EXPECT_TRUE(checkAnswer(0));
|
|
|
+
|
|
|
+ // 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);
|
|
|
+
|
|
|
+ uint32_t actual_extra = 0;
|
|
|
+ EXPECT_NO_THROW(context->getExtraParam("extra_test", actual_extra));
|
|
|
+ EXPECT_EQ(430, actual_extra);
|
|
|
+
|
|
|
+ // Create a configuration which "updates" all of the parameter values
|
|
|
+ // plus one unknown at the end.
|
|
|
+ string config2 = "{ \"bool_test\": false , "
|
|
|
+ " \"uint32_test\": 88 , "
|
|
|
+ " \"string_test\": \"ewww yuk!\" , "
|
|
|
+ " \"extra_test\": 11 , "
|
|
|
+ " \"zeta_unknown\": 33 } ";
|
|
|
+ ASSERT_TRUE(fromJSON(config2));
|
|
|
+
|
|
|
+ // Force a failure on the last element
|
|
|
+ SimFailure::set(SimFailure::ftElementUnknown);
|
|
|
+ answer_ = cfg_mgr_->parseConfig(config_set_);
|
|
|
+ EXPECT_TRUE(checkAnswer(1));
|
|
|
+
|
|
|
+ // Refresh our local pointer.
|
|
|
+ context = getStubContext();
|
|
|
+
|
|
|
+ // 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));
|
|
|
+ EXPECT_EQ("hmmm chewy", actual_string);
|
|
|
+
|
|
|
+ actual_extra = 0;
|
|
|
+ EXPECT_NO_THROW(context->getExtraParam("extra_test", actual_extra));
|
|
|
+ EXPECT_EQ(430, actual_extra);
|
|
|
+}
|
|
|
+
|
|
|
+} // end of anonymous namespace
|