Browse Source

[3436] Added configuration permutations test from file to D2

Added the unit test D2CfgMgrTest.configPermutations to
d2_cfg_mgr_unittests.cc.  This test iterates through the
list of test configurations defined in a specialzed JSON
data file.  It provides a relatively painless way to test
a large number configurations without hard-coding them.

Added the test data file:

It currently contains over sixty tests. The vast majority
of these tests are invalid content tests.
Thomas Markwalder 11 years ago
parent
commit
d1a0a7fa5c

+ 15 - 8
src/bin/d2/d2_config.cc

@@ -349,7 +349,8 @@ TSIGKeyInfoParser::build(isc::data::ConstElementPtr key_config) {
     // data to the parser's local storage.
     BOOST_FOREACH (config_pair, key_config->mapValue()) {
         isc::dhcp::ParserPtr parser(createConfigParser(config_pair.first,
-                                    config_pair.second->getPosition()));
+                                                       config_pair.second->
+                                                       getPosition()));
         parser->build(config_pair.second);
         parser->commit();
     }
@@ -387,8 +388,7 @@ TSIGKeyInfoParser::build(isc::data::ConstElementPtr key_config) {
     try {
         TSIGKeyInfo::stringToAlgorithmName(algorithm);
     } catch (const std::exception& ex) {
-        isc_throw(D2CfgError, "TSIG key invalid algorithm : "
-                  << algorithm << " : " << pos[1]);
+        isc_throw(D2CfgError, "TSIG key : " << ex.what() << " : " << pos[1]);
     }
 
     // Secret cannot be blank.
@@ -513,7 +513,8 @@ DnsServerInfoParser::build(isc::data::ConstElementPtr server_config) {
     // data to the parser's local storage.
     BOOST_FOREACH (config_pair, server_config->mapValue()) {
         isc::dhcp::ParserPtr parser(createConfigParser(config_pair.first,
-                                    config_pair.second->getPosition()));
+                                                       config_pair.second->
+                                                       getPosition()));
         parser->build(config_pair.second);
         parser->commit();
     }
@@ -682,7 +683,8 @@ DdnsDomainParser::build(isc::data::ConstElementPtr domain_config) {
     isc::dhcp::ConfigPair config_pair;
     BOOST_FOREACH(config_pair, domain_config->mapValue()) {
         isc::dhcp::ParserPtr parser(createConfigParser(config_pair.first,
-                                    config_pair.second->getPosition()));
+                                                       config_pair.second->
+                                                       getPosition()));
         parser->build(config_pair.second);
         parser->commit();
     }
@@ -838,7 +840,9 @@ DdnsDomainListMgrParser::build(isc::data::ConstElementPtr domain_config) {
     // data to the parser's local storage.
     isc::dhcp::ConfigPair config_pair;
     BOOST_FOREACH(config_pair, domain_config->mapValue()) {
-        isc::dhcp::ParserPtr parser(createConfigParser(config_pair.first));
+        isc::dhcp::ParserPtr parser(createConfigParser(config_pair.first,
+                                                       config_pair.second->
+                                                       getPosition()));
         parser->build(config_pair.second);
         parser->commit();
     }
@@ -848,7 +852,9 @@ DdnsDomainListMgrParser::build(isc::data::ConstElementPtr domain_config) {
 }
 
 isc::dhcp::ParserPtr
-DdnsDomainListMgrParser::createConfigParser(const std::string& config_id) {
+DdnsDomainListMgrParser::createConfigParser(const std::string& config_id,
+                                            const isc::data::Element::
+                                            Position& pos) {
     DhcpConfigParser* parser = NULL;
     if (config_id == "ddns_domains") {
        // Domain list parser is given our local domain storage. It will pass
@@ -857,7 +863,8 @@ DdnsDomainListMgrParser::createConfigParser(const std::string& config_id) {
        parser = new DdnsDomainListParser(config_id, local_domains_, keys_);
     } else {
        isc_throw(NotImplemented, "parser error: "
-                 "DdnsDomainListMgr parameter not supported: " << config_id);
+                 "DdnsDomainListMgr parameter not supported: " << config_id
+                 << " : " << pos);
     }
 
     // Return the new domain parser instance.

+ 8 - 2
src/bin/d2/d2_config.h

@@ -1145,10 +1145,16 @@ public:
     ///
     /// @param config_id is the "item_name" for a specific member element of
     /// the manager specification.
+    /// @param pos position within the configuration text (or file) of element
+    /// to be parsed.  This is passed for error messaging.
     ///
     /// @return returns a pointer to newly created parser.
-    virtual isc::dhcp::ParserPtr createConfigParser(const std::string&
-                                                    config_id);
+    ///
+    /// @throw D2CfgError if configuration contains an unknown parameter
+    virtual isc::dhcp::ParserPtr
+    createConfigParser(const std::string& config_id,
+                       const isc::data::Element::Position& pos =
+                       isc::data::Element::ZERO_POSITION());
 
     /// @brief Commits the configured DdsnDomainListMgr
     /// Currently this method is a NOP, as the manager instance is created

+ 1 - 0
src/bin/d2/tests/Makefile.am

@@ -12,6 +12,7 @@ noinst_SCRIPTS = d2_process_tests.sh
 
 EXTRA_DIST  = $(PYTESTS)
 EXTRA_DIST += d2_process_tests.sh.in
+EXTRA_DIST += testdata/d2_cfg_tests.json
 
 # Explicitly specify paths to dynamic libraries required by loadable python
 # modules. That is required on Mac OS systems. Otherwise we will get exception

+ 101 - 0
src/bin/d2/tests/d2_cfg_mgr_unittests.cc

@@ -32,6 +32,10 @@ std::string specfile(const std::string& name) {
     return (std::string(D2_SRC_DIR) + "/" + name);
 }
 
+std::string testDataFile(const std::string& name) {
+    return (std::string(D2_TEST_DATA_DIR) + "/" + name);
+}
+
 /// @brief Test fixture class for testing D2CfgMgr class.
 /// It maintains an member instance of D2CfgMgr and provides methods for
 /// converting JSON strings to configuration element sets, checking parse
@@ -1564,4 +1568,101 @@ TEST_F(D2CfgMgrTest, matchReverse) {
     ASSERT_THROW(cfg_mgr_->matchReverse("", match), D2CfgError);
 }
 
+/// @brief Tests D2 config parsing against a wide range of config permutations.
+/// It iterates over all of the test configurations described in given file.
+/// The file content is JSON specialized to this test. The format of the file
+/// is:
+///
+/// @code
+/// # The file must open with a list. It's name is arbitrary.
+///
+/// { "test_list" :
+/// [
+///
+/// #    Test one starts here:
+///      {
+///
+/// #    Each test has:
+/// #      1. description - optional text description
+/// #      2. should_fail - bool indicator if parsing is expected to file
+/// #         (defaults to false)
+/// #       3. data - configuration text to parse
+/// #
+///      "description" : "<text describing test>",
+///      "should_fail" : <true|false> ,
+///      "data" :
+///          {
+/// #        configuration elements here
+///          "bool_val" : false,
+///          "some_map" :  {}
+/// #         :
+///          }
+///      }
+///
+/// #    Next test would start here
+///      ,
+///      {
+///      }
+///
+/// ]}
+///
+/// @endcode
+///
+/// (The file supports comments per Element::fromJSONFile())
+///
+TEST_F(D2CfgMgrTest, configPermutations) {
+    std::string test_file = testDataFile("d2_cfg_tests.json");
+    isc::data::ConstElementPtr tests;
+
+    // Read contents of the file and parse it as JSON. Note it must contain
+    // all valid JSON, we aren't testing JSON parsing.
+    try {
+        tests = isc::data::Element::fromJSONFile(test_file, true);
+    } catch (const std::exception& ex) {
+        FAIL() << "ERROR parsing file : " << test_file << " : " << ex.what();
+    }
+
+    // Read in each test For each test, read:
+    //  1. description - optional text description
+    //  2. should_fail - bool indicator if parsing is expected to file (defaults
+    //     to false
+    //  3. data - configuration text to parse
+    //
+    // Next attempt to parse the configuration by passing it into
+    // D2CfgMgr::parseConfig().  Then check the parsing outcome against the
+    // expected outcome as given by should_fail.
+    isc::data::ConstElementPtr test;
+    BOOST_FOREACH(test, tests->get("test_list")->listValue()) {
+
+        // Grab the description.
+        std::string description = "<no desc>";
+        isc::data::ConstElementPtr elem = test->get("description");
+        if (elem) {
+            elem->getValue(description);
+        }
+
+        // Grab the outcome flag, should_fail, defaults to false if it's
+        // not specified.
+        bool should_fail = false;
+        elem = test->get("should_fail");
+        if (elem)  {
+            elem->getValue(should_fail);
+        }
+
+        // Grab the test's configuration data.
+        isc::data::ConstElementPtr data = test->get("data");
+        ASSERT_TRUE(data) << "No data for test: "
+                          << " : " << test->getPosition();
+
+        // Verify that we can parse the configuration.
+        answer_ = cfg_mgr_->parseConfig(data);
+        if (checkAnswer(!should_fail)) {
+            ADD_FAILURE() << "Parsing should have "
+                          << (should_fail ? "failed" : "passed")
+                          << " for : " << description
+                          << " : "  << test->getPosition();
+        }
+    }
+}
+
 } // end of anonymous namespace

+ 1 - 0
src/bin/d2/tests/test_data_files_config.h.in

@@ -15,3 +15,4 @@
 /// @brief Path to D2 source dir so tests against the dhcp-ddns.spec file
 /// can find it reliably.
 #define D2_SRC_DIR "@abs_top_srcdir@/src/bin/d2"
+#define D2_TEST_DATA_DIR "@abs_top_srcdir@/src/bin/d2/tests/testdata"

File diff suppressed because it is too large
+ 1273 - 0
src/bin/d2/tests/testdata/d2_cfg_tests.json