Commit fa1e32d9 authored by Francis Dupont's avatar Francis Dupont
Browse files

[master] Finished merge of trac4094 (class evaluator)

parents c4557eef 81975026
......@@ -13,6 +13,7 @@ AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
lib_LTLIBRARIES = libkea-eval.la
libkea_eval_la_SOURCES =
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
libkea_eval_la_SOURCES += parser.cc parser.h
......
// Copyright (C) 2015 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 <eval/evaluate.h>
namespace isc {
namespace dhcp {
bool evaluate(const Expression& expr, const Pkt& pkt) {
ValueStack values;
for (Expression::const_iterator it = expr.begin();
it != expr.end(); ++it) {
(*it)->evaluate(pkt, values);
}
if (values.size() != 1) {
isc_throw(EvalBadStack, "Incorrect stack order. Expected exactly "
"1 value at the end of evaluatuion, got " << values.size());
}
if (values.top() == "false") {
return (false);
} else if (values.top() == "true") {
return (true);
} else {
isc_throw(EvalTypeError, "Incorrect evaluation type. Expected "
"\"false\" or \"true\", got \"" << values.top() << "\"");
}
}
}; // end of isc::dhcp namespace
}; // end of isc namespace
// Copyright (C) 2015 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.
#ifndef EVALUATE_H
#define EVALUATE_H
#include <eval/token.h>
namespace isc {
namespace dhcp {
/// @brief Evaluate a RPN expression for a v4 or v6 packet and return
/// a true or false decision
///
/// @param expr the RPN expression, i.e., a vector of parsed tokens
/// @param pkt The v4 or v6 packet
/// @return the boolean decision
/// @throw EvalStackError if there is not exactly one element on the value
/// stack at the end of the evaluation
/// @throw EvalTypeError if the value at the top of the stack at the
/// end of the evaluation is not "false" or "true"
bool evaluate(const Expression& expr, const Pkt& pkt);
}; // end of isc::dhcp namespace
}; // end of isc namespace
#endif
......@@ -27,6 +27,7 @@ if HAVE_GTEST
TESTS += libeval_unittests
libeval_unittests_SOURCES = context_unittest.cc
libeval_unittests_SOURCES += evaluate_unittest.cc
libeval_unittests_SOURCES += token_unittest.cc
libeval_unittests_SOURCES += run_unittests.cc
libeval_unittests_CXXFLAGS = $(AM_CXXFLAGS)
......
// Copyright (C) 2015 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.h>
#include <eval/evaluate.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 evaluation.
///
/// This class provides several convenience objects to be used during testing
/// of the evaluation of classification expressions.
class EvaluateTest : public ::testing::Test {
public:
/// @brief Initializes Pkt4,Pkt6 and options that can be useful for
/// evaluation tests.
EvaluateTest() {
e_.clear();
pkt4_.reset(new Pkt4(DHCPDISCOVER, 12345));
pkt6_.reset(new Pkt6(DHCPV6_SOLICIT, 12345));
// Add options with easily identifiable strings in them
option_str4_.reset(new OptionString(Option::V4, 100, "hundred4"));
option_str6_.reset(new OptionString(Option::V6, 100, "hundred6"));
pkt4_->addOption(option_str4_);
pkt6_->addOption(option_str6_);
}
Expression e_; ///< An expression
bool result_; ///< A decision
Pkt4Ptr pkt4_; ///< A stub DHCPv4 packet
Pkt6Ptr pkt6_; ///< A stub DHCPv6 packet
OptionPtr option_str4_; ///< A string option for DHCPv4
OptionPtr option_str6_; ///< A string option for DHCPv6
/// @todo: Add more option types here
};
// This checks the empty expression: it should raise EvalBadStack
// when evaluated with a Pkt4. (The actual packet is not used)
TEST_F(EvaluateTest, empty4) {
ASSERT_THROW(evaluate(e_, *pkt4_), EvalBadStack);
}
// This checks the empty expression: it should raise EvalBadStack
// when evaluated with a Pkt6. (The actual packet is not used)
TEST_F(EvaluateTest, empty6) {
ASSERT_THROW(evaluate(e_, *pkt6_), EvalBadStack);
}
// This checks the { "false" } expression: it should return false
// when evaluated with a Pkt4. (The actual packet is not used)
TEST_F(EvaluateTest, false4) {
TokenPtr tfalse;
ASSERT_NO_THROW(tfalse.reset(new TokenString("false")));
e_.push_back(tfalse);
ASSERT_NO_THROW(result_ = evaluate(e_, *pkt4_));
EXPECT_FALSE(result_);
}
// This checks the { "false" } expression: it should return false
// when evaluated with a Pkt6. (The actual packet is not used)
TEST_F(EvaluateTest, false6) {
TokenPtr tfalse;
ASSERT_NO_THROW(tfalse.reset(new TokenString("false")));
e_.push_back(tfalse);
ASSERT_NO_THROW(result_ = evaluate(e_, *pkt6_));
EXPECT_FALSE(result_);
}
// This checks the { "true" } expression: it should return true
// when evaluated with a Pkt4. (The actual packet is not used)
TEST_F(EvaluateTest, true4) {
TokenPtr ttrue;
ASSERT_NO_THROW(ttrue.reset(new TokenString("true")));
e_.push_back(ttrue);
ASSERT_NO_THROW(result_ = evaluate(e_, *pkt4_));
EXPECT_TRUE(result_);
}
// This checks the { "true" } expression: it should return true
// when evaluated with a Pkt6. (The actual packet is not used)
TEST_F(EvaluateTest, true6) {
TokenPtr ttrue;
ASSERT_NO_THROW(ttrue.reset(new TokenString("true")));
e_.push_back(ttrue);
ASSERT_NO_THROW(result_ = evaluate(e_, *pkt6_));
EXPECT_TRUE(result_);
}
// This checks the evaluation must lead to "false" or "true"
// with a Pkt4. (The actual packet is not used)
TEST_F(EvaluateTest, bad4) {
TokenPtr bad;
ASSERT_NO_THROW(bad.reset(new TokenString("bad")));
e_.push_back(bad);
ASSERT_THROW(evaluate(e_, *pkt4_), EvalTypeError);
}
// This checks the evaluation must lead to "false" or "true"
// with a Pkt6. (The actual packet is not used)
TEST_F(EvaluateTest, bad6) {
TokenPtr bad;
ASSERT_NO_THROW(bad.reset(new TokenString("bad")));
e_.push_back(bad);
ASSERT_THROW(evaluate(e_, *pkt6_), EvalTypeError);
}
// This checks the evaluation must leave only one value on the stack
// with a Pkt4. (The actual packet is not used)
TEST_F(EvaluateTest, two4) {
TokenPtr ttrue;
ASSERT_NO_THROW(ttrue.reset(new TokenString("true")));
e_.push_back(ttrue);
e_.push_back(ttrue);
ASSERT_THROW(evaluate(e_, *pkt4_), EvalBadStack);
}
// This checks the evaluation must leave only one value on the stack
// with a Pkt6. (The actual packet is not used)
TEST_F(EvaluateTest, two6) {
TokenPtr ttrue;
ASSERT_NO_THROW(ttrue.reset(new TokenString("true")));
e_.push_back(ttrue);
e_.push_back(ttrue);
ASSERT_THROW(evaluate(e_, *pkt6_), EvalBadStack);
}
// A more complex test evaluated with a Pkt4. (The actual packet is not used)
TEST_F(EvaluateTest, compare4) {
TokenPtr tfoo;
TokenPtr tbar;
TokenPtr tequal;
ASSERT_NO_THROW(tfoo.reset(new TokenString("foo")));
e_.push_back(tfoo);
ASSERT_NO_THROW(tbar.reset(new TokenString("bar")));
e_.push_back(tbar);
ASSERT_NO_THROW(tequal.reset(new TokenEqual()));
e_.push_back(tequal);
ASSERT_NO_THROW(result_ = evaluate(e_, *pkt4_));
EXPECT_FALSE(result_);
}
// A more complex test evaluated with a Pkt6. (The actual packet is not used)
TEST_F(EvaluateTest, compare6) {
TokenPtr tfoo;
TokenPtr tbar;
TokenPtr tequal;
ASSERT_NO_THROW(tfoo.reset(new TokenString("foo")));
e_.push_back(tfoo);
ASSERT_NO_THROW(tbar.reset(new TokenString("bar")));
e_.push_back(tbar);
ASSERT_NO_THROW(tequal.reset(new TokenEqual()));
e_.push_back(tequal);
ASSERT_NO_THROW(result_ = evaluate(e_, *pkt6_));
EXPECT_FALSE(result_);
}
// A test using packets.
TEST_F(EvaluateTest, packet) {
TokenPtr toption;
TokenPtr tstring;
TokenPtr tequal;
ASSERT_NO_THROW(toption.reset(new TokenOption(100)));
e_.push_back(toption);
ASSERT_NO_THROW(tstring.reset(new TokenString("hundred4")));
e_.push_back(tstring);
ASSERT_NO_THROW(tequal.reset(new TokenEqual()));
e_.push_back(tequal);
ASSERT_NO_THROW(result_ = evaluate(e_, *pkt4_));
EXPECT_TRUE(result_);
ASSERT_NO_THROW(result_ = evaluate(e_, *pkt6_));
EXPECT_FALSE(result_);
}
// A test using substring on an option.
TEST_F(EvaluateTest, complex) {
TokenPtr toption;
TokenPtr tstart;
TokenPtr tlength;
TokenPtr tsubstring;
TokenPtr tstring;
TokenPtr tequal;
// Get the option, i.e., "hundred[46]"
ASSERT_NO_THROW(toption.reset(new TokenOption(100)));
e_.push_back(toption);
// Get substring("hundred[46]", 0, 7), i.e., "hundred"
ASSERT_NO_THROW(tstart.reset(new TokenString("0")));
e_.push_back(tstart);
ASSERT_NO_THROW(tlength.reset(new TokenString("7")));
e_.push_back(tlength);
ASSERT_NO_THROW(tsubstring.reset(new TokenSubstring()));
e_.push_back(tsubstring);
// Compare with "hundred"
ASSERT_NO_THROW(tstring.reset(new TokenString("hundred")));
e_.push_back(tstring);
ASSERT_NO_THROW(tequal.reset(new TokenEqual()));
e_.push_back(tequal);
// Should return true for v4 and v6 packets
ASSERT_NO_THROW(result_ = evaluate(e_, *pkt4_));
EXPECT_TRUE(result_);
ASSERT_NO_THROW(result_ = evaluate(e_, *pkt6_));
EXPECT_TRUE(result_);
}
};
......@@ -56,6 +56,7 @@ public:
isc::Exception(file, line, what) { };
};
/// @brief Base class for all tokens
///
/// It provides an interface for all tokens and storage for string representation
......
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