#include <config.h>
+#include <eval/dependency.h>
#include <dhcpsrv/client_class_def.h>
#include <dhcpsrv/cfgmgr.h>
#include <boost/foreach.hpp>
cfg_option_ = cfg_option;
}
+bool
+ClientClassDef::dependOnClass(const std::string& name) const {
+ return (isc::dhcp::dependOnClass(match_expr_, name));
+}
+
bool
ClientClassDef::equals(const ClientClassDef& other) const {
return ((name_ == other.name_) &&
return (list_);
}
+bool
+ClientClassDictionary::dependOnClass(const std::string& name,
+ std::string& depend) const {
+ // Skip previous classes as they should not depend on name.
+ bool found = false;
+ for (ClientClassDefList::iterator this_class = list_->begin();
+ this_class != list_->end(); ++this_class) {
+ if (found) {
+ if ((*this_class)->dependOnClass(name)) {
+ depend = (*this_class)->getName();
+ return (true);
+ }
+ } else {
+ if ((*this_class)->getName() == name) {
+ found = true;
+ }
+ }
+ }
+ return (false);
+}
+
bool
ClientClassDictionary::equals(const ClientClassDictionary& other) const {
if (list_->size() != other.list_->size()) {
/// @param cfg_option the option collection to assign the class
void setCfgOption(const CfgOptionPtr& cfg_option);
+ /// @brief Checks direct dependency.
+ ///
+ /// @param name The client class name.
+ ///
+ /// @return true if the definition depends on the class name, false if not.
+ bool dependOnClass(const std::string& name) const;
+
/// @brief Compares two @c ClientClassDef objects for equality.
///
/// @param other Other client class definition to compare to.
/// @return ClientClassDefListPtr to the list of classes
const ClientClassDefListPtr& getClasses() const;
+ /// @brief Checks direct dependency.
+ ///
+ /// @param name The client class name.
+ /// @param depend Set to the name of the first depending class.
+ ///
+ /// @return true if a definition depends on the class name, false if none.
+ bool dependOnClass(const std::string& name, std::string& depend) const;
+
/// @brief Compares two @c ClientClassDictionary objects for equality.
///
/// @param other Other client class definition to compare to.
cfg_option = cclass->getCfgOption();
ASSERT_TRUE(cfg_option);
EXPECT_TRUE(cfg_option->empty());
+
+ // Verify we don't depend on something.
+ EXPECT_FALSE(cclass->dependOnClass("foobar"));
+ EXPECT_FALSE(cclass->dependOnClass(""));
}
// Tests options operations. Note we just do the basics
EXPECT_TRUE(*cclass != *cclass2);
}
+// Verifies assignment.
+TEST(ClientClassDef, assign) {
+
+ boost::scoped_ptr<ClientClassDef> cclass;
+ ExpressionPtr expr;
+ CfgOptionPtr test_options;
+ OptionPtr opt;
+
+ // Make an expression
+ expr.reset(new Expression());
+ TokenPtr token(new TokenString("boo"));
+ expr->push_back(token);
+
+ // Create an option container with an option
+ OptionPtr option;
+ test_options.reset(new CfgOption());
+ option.reset(new Option(Option::V4, 17, OptionBuffer(10, 0xFF)));
+ ASSERT_NO_THROW(test_options->add(option, false, DHCP4_OPTION_SPACE));
+
+ // Now remake the client class with cfg_option
+ ASSERT_NO_THROW(cclass.reset(new ClientClassDef("class_one", expr,
+ test_options)));
+
+ // Now lets assign a fresh definition to it;
+ boost::scoped_ptr<ClientClassDef> cclass2;
+ ExpressionPtr expr_empty;
+ ASSERT_NO_THROW(cclass2.reset(new ClientClassDef("another", expr_empty)));
+ ASSERT_NO_THROW(*cclass2 = *cclass);
+
+ // The allocated Expression pointers should match
+ EXPECT_TRUE(cclass->getMatchExpr().get() ==
+ cclass2->getMatchExpr().get());
+
+ // The allocated CfgOption pointers should match
+ EXPECT_TRUE(cclass->getCfgOption().get() ==
+ cclass2->getCfgOption().get());
+
+ // Verify the equality tools reflect that the classes are equal.
+ EXPECT_TRUE(cclass->equals(*cclass2));
+ EXPECT_TRUE(*cclass == *cclass2);
+ EXPECT_FALSE(*cclass != *cclass2);
+
+ // Verify the required flag is enough to make classes not equal.
+ EXPECT_FALSE(cclass->getRequired());
+ cclass2->setRequired(true);
+ EXPECT_TRUE(cclass2->getRequired());
+ EXPECT_FALSE(*cclass == *cclass2);
+ EXPECT_TRUE(*cclass != *cclass2);
+ cclass2->setRequired(false);
+ EXPECT_TRUE(*cclass == *cclass2);
+
+ // Verify the depend on known flag is enough to make classes not equal.
+ EXPECT_FALSE(cclass->getDependOnKnown());
+ cclass2->setDependOnKnown(true);
+ EXPECT_TRUE(cclass2->getDependOnKnown());
+ EXPECT_FALSE(*cclass == *cclass2);
+ EXPECT_TRUE(*cclass != *cclass2);
+}
+
+// Tests dependency.
+TEST(ClientClassDef, dependency) {
+ boost::scoped_ptr<ClientClassDef> cclass;
+
+ ExpressionPtr expr;
+
+ // Make an expression
+ expr.reset(new Expression());
+ TokenPtr token(new TokenMember("foo"));
+ expr->push_back(token);
+
+ ASSERT_NO_THROW(cclass.reset(new ClientClassDef("class", expr)));
+ EXPECT_TRUE(cclass->dependOnClass("foo"));
+ EXPECT_FALSE(cclass->dependOnClass("bar"));
+}
+
// Tests the basic operation of ClientClassDictionary
// This includes adding, finding, and removing classes
EXPECT_TRUE(*dictionary != *dictionary2);
}
+// Tests dependency.
+TEST(ClientClassDictionary, dependency) {
+ ClientClassDictionaryPtr dictionary(new ClientClassDictionary());
+ ExpressionPtr expr;
+ CfgOptionPtr cfg_option;
+
+ // Make an expression depending on forward class.
+ ExpressionPtr expr1;
+ expr1.reset(new Expression());
+ TokenPtr token1(new TokenMember("cc2"));
+ expr1->push_back(token1);
+
+ ASSERT_NO_THROW(dictionary->addClass("cc1", expr1, "", false,
+ false, cfg_option));
+
+ // Make an expression depending on first class.
+ ExpressionPtr expr2;
+ expr2.reset(new Expression());
+ TokenPtr token2(new TokenMember("cc1"));
+ expr2->push_back(token2);
+
+ ASSERT_NO_THROW(dictionary->addClass("cc2", expr2, "", false,
+ false, cfg_option));
+
+ // Make expression with dependency.
+ ASSERT_NO_THROW(dictionary->addClass("cc3", expr, "", false,
+ false, cfg_option));
+
+ ExpressionPtr expr3;
+ expr3.reset(new Expression());
+ TokenPtr token3(new TokenMember("cc3"));
+ expr3->push_back(token3);
+
+ ASSERT_NO_THROW(dictionary->addClass("cc4", expr3, "", false,
+ false, cfg_option));
+
+ // Not matching dependency does not match.
+ string depend;
+ EXPECT_FALSE(dictionary->dependOnClass("foobar", depend));
+ EXPECT_TRUE(depend.empty());
+
+ // Forward dependency is ignored.
+ depend = "";
+ EXPECT_FALSE(dictionary->dependOnClass("cc2", depend));
+ EXPECT_TRUE(depend.empty());
+
+ // Backward dependency is detected.
+ depend = "";
+ EXPECT_TRUE(dictionary->dependOnClass("cc3", depend));
+ EXPECT_EQ("cc4", depend);
+}
+
// Tests the default constructor regarding fixed fields
TEST(ClientClassDef, fixedFieldsDefaults) {
boost::scoped_ptr<ClientClassDef> cclass;
lib_LTLIBRARIES = libkea-eval.la
libkea_eval_la_SOURCES =
+libkea_eval_la_SOURCES += dependency.cc dependency.h
libkea_eval_la_SOURCES += eval_log.cc eval_log.h
libkea_eval_la_SOURCES += evaluate.cc evaluate.h
libkea_eval_la_SOURCES += token.cc token.h
--- /dev/null
+// Copyright (C) 2018 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 <eval/dependency.h>
+#include <boost/pointer_cast.hpp>
+
+namespace isc {
+namespace dhcp {
+
+bool dependOnClass(const TokenPtr& token, const std::string& name) {
+ boost::shared_ptr<TokenMember> member;
+ member = boost::dynamic_pointer_cast<TokenMember>(token);
+ if (!member) {
+ return (false);
+ }
+ return (member->getClientClass() == name);
+}
+
+bool dependOnClass(const ExpressionPtr& expr, const std::string& name) {
+ if (!expr) {
+ return (false);
+ }
+ for (auto it = expr->cbegin(); it != expr->cend(); ++it) {
+ if (dependOnClass(*it, name)) {
+ return (true);
+ }
+ }
+ return (false);
+}
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
--- /dev/null
+// Copyright (C) 2018 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/.
+
+#ifndef DEPENDENCY_H
+#define DEPENDENCY_H
+
+#include <eval/token.h>
+#include <string>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Checks dependency on a token.
+///
+/// It checks if the token is a TokenMember for the given class.
+///
+/// @param token A pointer to the token.
+/// @param name The client class name.
+/// @return true if token points to a TokenMember of name, false if not.
+bool dependOnClass(const TokenPtr& token, const std::string& name);
+
+/// @brief Checks dependency on an expression.
+///
+/// It checks if a member of the expression depends on the given class.
+///
+/// @param expr An expression.
+/// @param name The client class name.
+/// @return true if a member of expr depends on name, false if not.
+bool dependOnClass(const ExpressionPtr& expr, const std::string& name);
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
+
+#endif
libeval_unittests_SOURCES = boolean_unittest.cc
libeval_unittests_SOURCES += context_unittest.cc
+libeval_unittests_SOURCES += dependency_unittest.cc
libeval_unittests_SOURCES += evaluate_unittest.cc
libeval_unittests_SOURCES += token_unittest.cc
libeval_unittests_SOURCES += run_unittests.cc
--- /dev/null
+// Copyright (C) 2018 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 <eval/dependency.h>
+#include <eval/eval_context.h>
+#include <eval/token.h>
+#include <dhcp/pkt4.h>
+#include <dhcp/pkt6.h>
+#include <dhcp/dhcp4.h>
+#include <dhcp/dhcp6.h>
+#include <dhcp/option_string.h>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc::dhcp;
+
+namespace {
+
+/// @brief Test fixture for testing dependency.
+///
+/// This class provides several convenience objects to be used during testing
+/// of the dependcy of classification expressions.
+class DependencyTest : public ::testing::Test {
+public:
+
+ /// @brief Reset expression and result.
+ ~DependencyTest() {
+ e_.reset();
+ result_ = false;
+ }
+
+ ExpressionPtr e_; ///< An expression
+
+ bool result_; ///< A decision
+};
+
+// This checks the null expression: it should return false.
+TEST_F(DependencyTest, nullExpr) {
+ TokenPtr token;
+ ASSERT_NO_THROW(result_ = dependOnClass(token, "foobar"));
+ EXPECT_FALSE(result_);
+ ASSERT_NO_THROW(result_ = dependOnClass(e_, "foobar"));
+ EXPECT_FALSE(result_);
+}
+
+// This checks the empty expression: it should return false.
+TEST_F(DependencyTest, emptyExpr) {
+ e_.reset(new Expression());
+ ASSERT_NO_THROW(result_ = dependOnClass(e_, "foobar"));
+ EXPECT_FALSE(result_);
+}
+
+// This checks the { "true" } expression: it should return false.
+TEST_F(DependencyTest, trueExpr) {
+ TokenPtr ttrue;
+ ASSERT_NO_THROW(ttrue.reset(new TokenString("true")));
+ ASSERT_NO_THROW(result_ = dependOnClass(ttrue, "foobar"));
+ EXPECT_FALSE(result_);
+ e_.reset(new Expression());
+ e_->push_back(ttrue);
+ ASSERT_NO_THROW(result_ = dependOnClass(e_, "foobar"));
+ EXPECT_FALSE(result_);
+}
+
+// This checks the { member('not-matching') } expression:
+// it should return false.
+TEST_F(DependencyTest, notMatching) {
+ TokenPtr notmatching;
+ ASSERT_NO_THROW(notmatching.reset(new TokenMember("not-matching")));
+ ASSERT_NO_THROW(result_ = dependOnClass(notmatching, "foobar"));
+ EXPECT_FALSE(result_);
+ e_.reset(new Expression());
+ e_->push_back(notmatching);
+ ASSERT_NO_THROW(result_ = dependOnClass(e_, "foobar"));
+ EXPECT_FALSE(result_);
+}
+
+// This checks the { member('foobar') } expression: it should return true.
+TEST_F(DependencyTest, matching) {
+ TokenPtr matching;
+ ASSERT_NO_THROW(matching.reset(new TokenMember("foobar")));
+ ASSERT_NO_THROW(result_ = dependOnClass(matching, "foobar"));
+ EXPECT_TRUE(result_);
+ e_.reset(new Expression());
+ e_->push_back(matching);
+ result_ = false;
+ ASSERT_NO_THROW(result_ = dependOnClass(e_, "foobar"));
+ EXPECT_TRUE(result_);
+}
+
+};