Commit 0c0ae469 authored by Francis Dupont's avatar Francis Dupont Committed by Marcin Siodelski
Browse files

[5685] Added dependency methods and tests

parent 9ca5257c
......@@ -6,6 +6,7 @@
#include <config.h>
#include <eval/dependency.h>
#include <dhcpsrv/client_class_def.h>
#include <dhcpsrv/cfgmgr.h>
#include <boost/foreach.hpp>
......@@ -136,6 +137,11 @@ ClientClassDef::setCfgOption(const CfgOptionPtr& cfg_option) {
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_) &&
......@@ -279,6 +285,27 @@ ClientClassDictionary::getClasses() const {
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()) {
......
......@@ -116,6 +116,13 @@ public:
/// @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.
......@@ -323,6 +330,14 @@ public:
/// @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.
......
......@@ -50,6 +50,10 @@ TEST(ClientClassDef, construction) {
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
......@@ -222,6 +226,81 @@ TEST(ClientClassDef, copyAndEquality) {
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
......@@ -346,6 +425,58 @@ TEST(ClientClassDictionary, copyAndEquality) {
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;
......
......@@ -13,6 +13,7 @@ AM_CXXFLAGS += $(WARNING_GCC_44_STRICT_ALIASING_CFLAG)
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
......
// 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
// 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
......@@ -22,6 +22,7 @@ TESTS += libeval_unittests
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
......
// 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_);
}
};
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment