From 411e3f61132dfe995fc70b448eba2ba1750a250b Mon Sep 17 00:00:00 2001 From: Noah Attwood Date: Mon, 8 May 2023 23:27:20 -0300 Subject: [PATCH 01/14] improves code commenting and redefines derived expression node types --- include/attwoodn/expression_tree.hpp | 29 ++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/include/attwoodn/expression_tree.hpp b/include/attwoodn/expression_tree.hpp index 00e703e..043aaf2 100644 --- a/include/attwoodn/expression_tree.hpp +++ b/include/attwoodn/expression_tree.hpp @@ -51,21 +51,42 @@ namespace attwoodn::expression_tree { } } - namespace tree { + namespace node { /** - * The base class representing expression tree nodes + * @brief The base class representing all expression tree nodes. */ template class expression_tree_node_base { public: virtual ~expression_tree_node_base() {}; + + /** + * @brief Evaluates the given object to determine if it satisfies the expressions defined in this node and all child nodes. + * + * @returns True if the given object satisfied the expression in this node and all the expressions of all nodes + * under this node in the expression tree; + * + * False if the given object did not satisfy the epression in this node and all the expressions of all + * nodes under this node in the expression tree. + */ virtual bool evaluate(const Obj& obj) = 0; }; - class expression_tree_op_node; - class expression_tree_leaf_node; + /** + * + */ + template + class expression_tree_op_node : public expression_tree_node_base { + + }; + template + class expression_tree_leaf_node : public expression_tree_node_base { + + }; } + + } \ No newline at end of file From 84d51ac54309fa0be9d322f94d4cf44ebd2255d4 Mon Sep 17 00:00:00 2001 From: Noah Attwood Date: Tue, 9 May 2023 00:04:12 -0300 Subject: [PATCH 02/14] implements expression tree operation nodes --- include/attwoodn/expression_tree.hpp | 78 ++++++++++++++++++++++++++-- 1 file changed, 74 insertions(+), 4 deletions(-) diff --git a/include/attwoodn/expression_tree.hpp b/include/attwoodn/expression_tree.hpp index 043aaf2..9f18920 100644 --- a/include/attwoodn/expression_tree.hpp +++ b/include/attwoodn/expression_tree.hpp @@ -1,5 +1,7 @@ #pragma once +#include + namespace attwoodn::expression_tree { namespace op { @@ -51,6 +53,11 @@ namespace attwoodn::expression_tree { } } + enum class boolean_op { + AND, + OR + }; + namespace node { /** @@ -64,23 +71,86 @@ namespace attwoodn::expression_tree { /** * @brief Evaluates the given object to determine if it satisfies the expressions defined in this node and all child nodes. * - * @returns True if the given object satisfied the expression in this node and all the expressions of all nodes + * @returns True if the given object satisfied the expression in this node and the expressions of all nodes * under this node in the expression tree; * - * False if the given object did not satisfy the epression in this node and all the expressions of all + * False if the given object did not satisfy the expression in this node and the expressions of all * nodes under this node in the expression tree. */ virtual bool evaluate(const Obj& obj) = 0; }; /** - * + * @brief Represents inner boolean operation nodes of the tree. These nodes contain references to a left and right + * child node, as well as the boolean operation to be performed (e.g. left child AND right child, or left child OR right child). */ template class expression_tree_op_node : public expression_tree_node_base { - + public: + using this_type = expression_tree_op_node; + + expression_tree_op_node(boolean_op bool_op) + : bool_op_(bool_op) {} + + expression_tree_op_node(const expression_tree_op_node& other) { + bool_op_ = other.bool_op_; + delete left_; + delete right_; + left_ = new LeftChild(*other.left_); + right_ = new RightChild(*other.right_); + } + + ~expression_tree_op_node() override { + delete left_; + delete right_; + } + + void set_right(RightChild* r) { + delete right_; + right_ = new RightChild(*r); + delete r; + } + + void set_left(LeftChild* l) { + delete left_; + left_ = new LeftChild(*l); + delete l; + } + + bool evaluate(const Obj& obj) override { + if(!left_ || !right_) { + throw std::runtime_error("expression_tree_op_node has a missing child node"); + } + + switch(bool_op_) { + case boolean_op::AND: { + return left_->evaluate(obj) && right_->evaluate(obj); + } + + case boolean_op::OR: { + return left_->evaluate(obj) || right_->evaluate(obj); + } + + default: { + throw std::runtime_error("expression_tree_op_node contained a non-implemented boolean expression"); + } + } + + return false; + } + + + private: + boolean_op bool_op_; + LeftChild* left_ { nullptr }; + RightChild* right_ { nullptr }; }; + /** + * @brief Represents leaf nodes of the tree. These nodes contain a reference to a member variable or member function of the + * given Obj type, the requested logical operation to be performed (e.g. equals, greater_than, etc.), and the + * value to compare to the given member variable or member function of an Obj instance. + */ template class expression_tree_leaf_node : public expression_tree_node_base { From 95a1a5495c088771b21a7ed938fffb159c405447 Mon Sep 17 00:00:00 2001 From: Noah Attwood Date: Tue, 9 May 2023 00:34:20 -0300 Subject: [PATCH 03/14] implements expression tree leaf node class. Now to implement the boolean operation logic to tie it all together --- include/attwoodn/expression_tree.hpp | 55 +++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/include/attwoodn/expression_tree.hpp b/include/attwoodn/expression_tree.hpp index 9f18920..5a7b430 100644 --- a/include/attwoodn/expression_tree.hpp +++ b/include/attwoodn/expression_tree.hpp @@ -60,6 +60,12 @@ namespace attwoodn::expression_tree { namespace node { + template + class expression_tree_op_node; + + template + class expression_tree_leaf_node; + /** * @brief The base class representing all expression tree nodes. */ @@ -139,7 +145,6 @@ namespace attwoodn::expression_tree { return false; } - private: boolean_op bool_op_; LeftChild* left_ { nullptr }; @@ -153,7 +158,55 @@ namespace attwoodn::expression_tree { */ template class expression_tree_leaf_node : public expression_tree_node_base { + public: + using this_type = expression_tree_leaf_node; + + /** + * @brief Constructor that accepts a reference to a member variable of Obj + */ + expression_tree_leaf_node(CompValue Obj::* obj_mem_var, Op op, CompValue comp_value) + : member_var_(obj_mem_var), + logical_op_(op), + comp_value_(comp_value) {} + + /** + * @brief Constructor that accepts a reference to a const member function of Obj + */ + expression_tree_leaf_node(CompValue (Obj::* obj_mem_func)() const, Op op, CompValue comp_value) + : member_func_(obj_mem_func), + logical_op_(op), + comp_value_(comp_value) {} + ~expression_tree_leaf_node() override {}; + + bool evaluate(const Obj& obj) override { + if (!member_func_ && !member_var_) { + throw std::runtime_error("expression_tree_leaf_node has a nullptr for both member function reference " + + std::string("and member variable reference. At least one is required")); + } + + CompValue actual_value; + + if (member_func_) { + // invoke member function and store the result + actual_value = (obj.*member_func_)(); + } + + else if (member_var_) { + // get value from obj's data member + actual_value = obj.*member_var_; + } + + else return false; + + return Op(actual_value, comp_value_); + } + + private: + CompValue (Obj::* member_func_)() const = nullptr; + CompValue Obj::* member_var_ = nullptr; + Op logical_op_; + CompValue comp_value_; }; } From 646354163a955500bc72c1d1f5f33b5f778fd540 Mon Sep 17 00:00:00 2001 From: Noah Attwood Date: Tue, 9 May 2023 10:11:11 -0300 Subject: [PATCH 04/14] adds basic initial test suite for expression_tree_leaf_node --- include/attwoodn/expression_tree.hpp | 9 ++++++-- tests/CMakeLists.txt | 4 ++-- tests/expression_tree_leaf_node.cpp | 34 ++++++++++++++++++++++++++++ tests/test.cpp | 7 ------ 4 files changed, 43 insertions(+), 11 deletions(-) create mode 100644 tests/expression_tree_leaf_node.cpp delete mode 100644 tests/test.cpp diff --git a/include/attwoodn/expression_tree.hpp b/include/attwoodn/expression_tree.hpp index 5a7b430..e69ce6b 100644 --- a/include/attwoodn/expression_tree.hpp +++ b/include/attwoodn/expression_tree.hpp @@ -180,7 +180,12 @@ namespace attwoodn::expression_tree { ~expression_tree_leaf_node() override {}; bool evaluate(const Obj& obj) override { - if (!member_func_ && !member_var_) { + if (member_func_ && member_var_) { + throw std::runtime_error("expression_tree_leaf_node has both a member function reference " + + std::string("and member variable reference. Only one is permitted")); + } + + else if (!member_func_ && !member_var_) { throw std::runtime_error("expression_tree_leaf_node has a nullptr for both member function reference " + std::string("and member variable reference. At least one is required")); } @@ -199,7 +204,7 @@ namespace attwoodn::expression_tree { else return false; - return Op(actual_value, comp_value_); + return logical_op_(actual_value, comp_value_); } private: diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9ddca6f..51a7ff8 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,6 +1,6 @@ if(BUILD_TESTING) - add_executable( expression_tree_test test.cpp ) - add_test( expression_tree_test ${EXECUTABLE_OUTPUT_PATH}/expression_tree_test ) + add_executable( expression_tree_leaf_node_test expression_tree_leaf_node.cpp ) + add_test( expression_tree_leaf_node_test ${EXECUTABLE_OUTPUT_PATH}/expression_tree_leaf_node_test ) add_executable( operators_test operators.cpp ) add_test( operators_test ${EXECUTABLE_OUTPUT_PATH}/operators_test ) diff --git a/tests/expression_tree_leaf_node.cpp b/tests/expression_tree_leaf_node.cpp new file mode 100644 index 0000000..d5a5254 --- /dev/null +++ b/tests/expression_tree_leaf_node.cpp @@ -0,0 +1,34 @@ +#include +#include +#include + +using namespace attwoodn::expression_tree; + +struct test_fixture { + std::string some_string; +}; + +template struct type_id{typedef T type;}; + +// template::type> +// node::expression_tree_leaf_node* make_leaf_node( CompValue Obj::* member_var, Op op, CompValue comp_value ) { +// return new node::expression_tree_leaf_node( member_var, op, comp_value ); +// } + +template::type> +node::expression_tree_leaf_node* make_leaf_node( CompValue Obj::* member_var, Op op, CompValue comp_value ) { + return new node::expression_tree_leaf_node( member_var, op, comp_value ); +} + +int main(int argc, char** argv) { + test_fixture fixture; + fixture.some_string = "hello world!"; + + auto node = make_leaf_node(&test_fixture::some_string, &op::equals, std::string("hello world!")); + assert(node->evaluate(fixture)); + + fixture.some_string = "hey, world!"; + assert(!node->evaluate(fixture)); + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/tests/test.cpp b/tests/test.cpp deleted file mode 100644 index 997deb8..0000000 --- a/tests/test.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include - -int main(int argc, char** argv) { - std::cout << "hello world!" << std::endl; - - return EXIT_SUCCESS; -} \ No newline at end of file From 62c6bf033133c56c13a63686ec68fdf80fac5bf3 Mon Sep 17 00:00:00 2001 From: Noah Attwood Date: Tue, 9 May 2023 10:22:36 -0300 Subject: [PATCH 05/14] implements template magic for is_convertible on make_leaf_node function --- tests/expression_tree_leaf_node.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/expression_tree_leaf_node.cpp b/tests/expression_tree_leaf_node.cpp index d5a5254..c160064 100644 --- a/tests/expression_tree_leaf_node.cpp +++ b/tests/expression_tree_leaf_node.cpp @@ -6,14 +6,16 @@ using namespace attwoodn::expression_tree; struct test_fixture { std::string some_string; + char* some_char_ptr; }; template struct type_id{typedef T type;}; -// template::type> -// node::expression_tree_leaf_node* make_leaf_node( CompValue Obj::* member_var, Op op, CompValue comp_value ) { -// return new node::expression_tree_leaf_node( member_var, op, comp_value ); -// } +template::type, + typename std::enable_if::value, int>::type = 0> +node::expression_tree_leaf_node* make_leaf_node( CompValue Obj::* member_var, Op op, CompValue comp_value ) { + return new node::expression_tree_leaf_node( member_var, op, comp_value ); +} template::type> node::expression_tree_leaf_node* make_leaf_node( CompValue Obj::* member_var, Op op, CompValue comp_value ) { From e13f11541c6d1448ad625d68939ad3d63f918a68 Mon Sep 17 00:00:00 2001 From: Noah Attwood Date: Tue, 9 May 2023 10:57:23 -0300 Subject: [PATCH 06/14] adds expression tree leaf node tests for evaluating char* type --- include/attwoodn/expression_tree.hpp | 11 +++ tests/expression_tree_leaf_node.cpp | 126 ++++++++++++++++++++++++--- 2 files changed, 125 insertions(+), 12 deletions(-) diff --git a/include/attwoodn/expression_tree.hpp b/include/attwoodn/expression_tree.hpp index e69ce6b..5af913d 100644 --- a/include/attwoodn/expression_tree.hpp +++ b/include/attwoodn/expression_tree.hpp @@ -216,5 +216,16 @@ namespace attwoodn::expression_tree { } + template struct type_id{typedef T type;}; + template::type, + typename std::enable_if::value, int>::type = 0> + node::expression_tree_leaf_node* make_expr( CompValue Obj::* member_var, Op op, CompValue comp_value ) { + return new node::expression_tree_leaf_node( member_var, op, comp_value ); + } + + template::type> + node::expression_tree_leaf_node* make_expr( CompValue Obj::* member_var, Op op, CompValue comp_value ) { + return new node::expression_tree_leaf_node( member_var, op, comp_value ); + } } \ No newline at end of file diff --git a/tests/expression_tree_leaf_node.cpp b/tests/expression_tree_leaf_node.cpp index c160064..a410ff7 100644 --- a/tests/expression_tree_leaf_node.cpp +++ b/tests/expression_tree_leaf_node.cpp @@ -9,28 +9,130 @@ struct test_fixture { char* some_char_ptr; }; -template struct type_id{typedef T type;}; +void test_string_evaluation(); +void test_char_ptr_evaluation(); -template::type, - typename std::enable_if::value, int>::type = 0> -node::expression_tree_leaf_node* make_leaf_node( CompValue Obj::* member_var, Op op, CompValue comp_value ) { - return new node::expression_tree_leaf_node( member_var, op, comp_value ); -} +int main(int argc, char** argv) { + test_string_evaluation(); + test_char_ptr_evaluation(); -template::type> -node::expression_tree_leaf_node* make_leaf_node( CompValue Obj::* member_var, Op op, CompValue comp_value ) { - return new node::expression_tree_leaf_node( member_var, op, comp_value ); + return EXIT_SUCCESS; } -int main(int argc, char** argv) { +void test_string_evaluation() { test_fixture fixture; fixture.some_string = "hello world!"; - auto node = make_leaf_node(&test_fixture::some_string, &op::equals, std::string("hello world!")); + auto node = make_expr(&test_fixture::some_string, &op::equals, std::string("hello world!")); assert(node->evaluate(fixture)); fixture.some_string = "hey, world!"; assert(!node->evaluate(fixture)); +} - return EXIT_SUCCESS; +void test_char_ptr_evaluation() { + char a = 'a'; + char a_too = 'a'; + char b = 'b'; + char b_too = 'b'; + char c = 'c'; + char c_too = 'c'; + + test_fixture fixture; + + // test equals + { + // make an expression : given char* == 'a' + auto expr = make_expr(&test_fixture::some_char_ptr, &op::equals, &a); + + fixture.some_char_ptr = &a; + assert(expr->evaluate(fixture)); + + fixture.some_char_ptr = &a_too; + assert(expr->evaluate(fixture)); + + fixture.some_char_ptr = &b; + assert(!expr->evaluate(fixture)); + + fixture.some_char_ptr = &b_too; + assert(!expr->evaluate(fixture)); + + fixture.some_char_ptr = &c; + assert(!expr->evaluate(fixture)); + + fixture.some_char_ptr = &c_too; + assert(!expr->evaluate(fixture)); + } + + // test not_equals + { + // make an expression : given char* != 'a' + auto expr = make_expr(&test_fixture::some_char_ptr, &op::not_equals, &a); + + fixture.some_char_ptr = &a; + assert(!expr->evaluate(fixture)); + + fixture.some_char_ptr = &a_too; + assert(!expr->evaluate(fixture)); + + fixture.some_char_ptr = &b; + assert(expr->evaluate(fixture)); + + fixture.some_char_ptr = &b_too; + assert(expr->evaluate(fixture)); + + fixture.some_char_ptr = &c; + assert(expr->evaluate(fixture)); + + fixture.some_char_ptr = &c_too; + assert(expr->evaluate(fixture)); + } + + // test less_than + { + // make an expression : given char* < 'b' + auto expr = make_expr(&test_fixture::some_char_ptr, &op::less_than, &b); + + fixture.some_char_ptr = &a; + assert(expr->evaluate(fixture)); + + fixture.some_char_ptr = &a_too; + assert(expr->evaluate(fixture)); + + fixture.some_char_ptr = &b; + assert(!expr->evaluate(fixture)); + + fixture.some_char_ptr = &b_too; + assert(!expr->evaluate(fixture)); + + fixture.some_char_ptr = &c; + assert(!expr->evaluate(fixture)); + + fixture.some_char_ptr = &c_too; + assert(!expr->evaluate(fixture)); + } + + // test greater_than + { + // make an expression : given char* > 'b' + auto expr = make_expr(&test_fixture::some_char_ptr, &op::greater_than, &b); + + fixture.some_char_ptr = &a; + assert(!expr->evaluate(fixture)); + + fixture.some_char_ptr = &a_too; + assert(!expr->evaluate(fixture)); + + fixture.some_char_ptr = &b; + assert(!expr->evaluate(fixture)); + + fixture.some_char_ptr = &b_too; + assert(!expr->evaluate(fixture)); + + fixture.some_char_ptr = &c; + assert(expr->evaluate(fixture)); + + fixture.some_char_ptr = &c_too; + assert(expr->evaluate(fixture)); + } } \ No newline at end of file From 14ea9f08838dbee2b322477e1ca16f0babc8e4f0 Mon Sep 17 00:00:00 2001 From: Noah Attwood Date: Tue, 9 May 2023 11:29:48 -0300 Subject: [PATCH 07/14] adds support for const member variable types, and adds new unit test cases for const strings --- include/attwoodn/expression_tree.hpp | 16 ++-- tests/expression_tree_leaf_node.cpp | 138 ++++++++++++++++++++++++++- 2 files changed, 141 insertions(+), 13 deletions(-) diff --git a/include/attwoodn/expression_tree.hpp b/include/attwoodn/expression_tree.hpp index 5af913d..45c1b12 100644 --- a/include/attwoodn/expression_tree.hpp +++ b/include/attwoodn/expression_tree.hpp @@ -7,7 +7,7 @@ namespace attwoodn::expression_tree { namespace op { template - inline bool less_than(T a, T b) { + inline bool less_than(const T a, const T b) { return a < b; } @@ -18,7 +18,7 @@ namespace attwoodn::expression_tree { } template - inline bool greater_than(T a, T b) { + inline bool greater_than(const T a, const T b) { return a > b; } @@ -29,7 +29,7 @@ namespace attwoodn::expression_tree { } template - inline bool equals(T a, T b) { + inline bool equals(const T a, const T b) { return a == b; } @@ -41,7 +41,7 @@ namespace attwoodn::expression_tree { } template - inline bool not_equals(T a, T b) { + inline bool not_equals(const T a, const T b) { return a != b; } @@ -164,7 +164,7 @@ namespace attwoodn::expression_tree { /** * @brief Constructor that accepts a reference to a member variable of Obj */ - expression_tree_leaf_node(CompValue Obj::* obj_mem_var, Op op, CompValue comp_value) + expression_tree_leaf_node(const CompValue Obj::* obj_mem_var, Op op, CompValue comp_value) : member_var_(obj_mem_var), logical_op_(op), comp_value_(comp_value) {} @@ -209,7 +209,7 @@ namespace attwoodn::expression_tree { private: CompValue (Obj::* member_func_)() const = nullptr; - CompValue Obj::* member_var_ = nullptr; + const CompValue Obj::* member_var_ = nullptr; Op logical_op_; CompValue comp_value_; }; @@ -219,13 +219,13 @@ namespace attwoodn::expression_tree { template struct type_id{typedef T type;}; template::type, - typename std::enable_if::value, int>::type = 0> + typename std::enable_if::value, int>::type = 0> node::expression_tree_leaf_node* make_expr( CompValue Obj::* member_var, Op op, CompValue comp_value ) { return new node::expression_tree_leaf_node( member_var, op, comp_value ); } template::type> - node::expression_tree_leaf_node* make_expr( CompValue Obj::* member_var, Op op, CompValue comp_value ) { + node::expression_tree_leaf_node* make_expr( const CompValue Obj::* member_var, Op op, CompValue comp_value ) { return new node::expression_tree_leaf_node( member_var, op, comp_value ); } } \ No newline at end of file diff --git a/tests/expression_tree_leaf_node.cpp b/tests/expression_tree_leaf_node.cpp index a410ff7..b4a974c 100644 --- a/tests/expression_tree_leaf_node.cpp +++ b/tests/expression_tree_leaf_node.cpp @@ -6,6 +6,7 @@ using namespace attwoodn::expression_tree; struct test_fixture { std::string some_string; + const std::string some_const_string = "this IS 4 T3s7 $tRing "; char* some_char_ptr; }; @@ -21,13 +22,140 @@ int main(int argc, char** argv) { void test_string_evaluation() { test_fixture fixture; - fixture.some_string = "hello world!"; - auto node = make_expr(&test_fixture::some_string, &op::equals, std::string("hello world!")); - assert(node->evaluate(fixture)); + // test const string + { + // test equals + { + auto expr1 = make_expr(&test_fixture::some_const_string, &op::equals, std::string("hello world!")); + assert(!expr1->evaluate(fixture)); + + auto expr2 = make_expr(&test_fixture::some_const_string, &op::equals, std::string("h")); + assert(!expr2->evaluate(fixture)); + + auto expr3 = make_expr(&test_fixture::some_const_string, &op::equals, std::string("t")); + assert(!expr3->evaluate(fixture)); + + auto expr4 = make_expr(&test_fixture::some_const_string, &op::equals, std::string("this ")); + assert(!expr4->evaluate(fixture)); + + auto expr5 = make_expr(&test_fixture::some_const_string, &op::equals, std::string("this IS 4 T3s7 $tRing ")); + assert(!expr5->evaluate(fixture)); + + auto expr6 = make_expr(&test_fixture::some_const_string, &op::equals, std::string("this IS 4 T3s7 $tRing")); + assert(!expr6->evaluate(fixture)); + + auto expr7 = make_expr(&test_fixture::some_const_string, &op::equals, std::string("this IS 4 T3s7 $tRing ")); + assert(expr7->evaluate(fixture)); + + auto expr8 = make_expr(&test_fixture::some_const_string, &op::equals, fixture.some_const_string); + assert(expr8->evaluate(fixture)); + } + + // test not_equals + { + auto expr1 = make_expr(&test_fixture::some_const_string, &op::not_equals, std::string("hello world!")); + assert(expr1->evaluate(fixture)); + + auto expr2 = make_expr(&test_fixture::some_const_string, &op::not_equals, std::string("h")); + assert(expr2->evaluate(fixture)); + + auto expr3 = make_expr(&test_fixture::some_const_string, &op::not_equals, std::string("t")); + assert(expr3->evaluate(fixture)); + + auto expr4 = make_expr(&test_fixture::some_const_string, &op::not_equals, std::string("this ")); + assert(expr4->evaluate(fixture)); + + auto expr5 = make_expr(&test_fixture::some_const_string, &op::not_equals, std::string("this IS 4 T3s7 $tRing ")); + assert(expr5->evaluate(fixture)); + + auto expr6 = make_expr(&test_fixture::some_const_string, &op::not_equals, std::string("this IS 4 T3s7 $tRing")); + assert(expr6->evaluate(fixture)); + + auto expr7 = make_expr(&test_fixture::some_const_string, &op::not_equals, std::string("this IS 4 T3s7 $tRing ")); + assert(!expr7->evaluate(fixture)); + + auto expr8 = make_expr(&test_fixture::some_const_string, &op::not_equals, fixture.some_const_string); + assert(!expr8->evaluate(fixture)); + } + + // test less_than + { + auto expr1 = make_expr(&test_fixture::some_const_string, &op::less_than, std::string("u")); + assert(expr1->evaluate(fixture)); + + auto expr2 = make_expr(&test_fixture::some_const_string, &op::less_than, std::string("z")); + assert(expr2->evaluate(fixture)); + + auto expr3 = make_expr(&test_fixture::some_const_string, &op::less_than, std::string("this IS 4 T3s7 $tRing ")); + assert(expr3->evaluate(fixture)); + + auto expr4 = make_expr(&test_fixture::some_const_string, &op::less_than, std::string("1234567890")); + assert(!expr4->evaluate(fixture)); + + auto expr5 = make_expr(&test_fixture::some_const_string, &op::less_than, std::string("A")); + assert(!expr5->evaluate(fixture)); - fixture.some_string = "hey, world!"; - assert(!node->evaluate(fixture)); + auto expr6 = make_expr(&test_fixture::some_const_string, &op::less_than, std::string("this ")); + assert(!expr6->evaluate(fixture)); + + auto expr7 = make_expr(&test_fixture::some_const_string, &op::less_than, std::string("stuff")); + assert(!expr7->evaluate(fixture)); + + auto expr8 = make_expr(&test_fixture::some_const_string, &op::less_than, std::string("abcdefghijklmnopqrstuvwxyz")); + assert(!expr8->evaluate(fixture)); + + auto expr9 = make_expr(&test_fixture::some_const_string, &op::less_than, std::string("this IS 4 T3s7 $tRing ")); + assert(!expr9->evaluate(fixture)); + + auto expr10 = make_expr(&test_fixture::some_const_string, &op::less_than, fixture.some_const_string); + assert(!expr10->evaluate(fixture)); + } + + // test greater_than + { + auto expr1 = make_expr(&test_fixture::some_const_string, &op::greater_than, std::string("u")); + assert(!expr1->evaluate(fixture)); + + auto expr2 = make_expr(&test_fixture::some_const_string, &op::greater_than, std::string("z")); + assert(!expr2->evaluate(fixture)); + + auto expr3 = make_expr(&test_fixture::some_const_string, &op::greater_than, std::string("this IS 4 T3s7 $tRing ")); + assert(!expr3->evaluate(fixture)); + + auto expr4 = make_expr(&test_fixture::some_const_string, &op::greater_than, std::string("1234567890")); + assert(expr4->evaluate(fixture)); + + auto expr5 = make_expr(&test_fixture::some_const_string, &op::greater_than, std::string("A")); + assert(expr5->evaluate(fixture)); + + auto expr6 = make_expr(&test_fixture::some_const_string, &op::greater_than, std::string("this ")); + assert(expr6->evaluate(fixture)); + + auto expr7 = make_expr(&test_fixture::some_const_string, &op::greater_than, std::string("stuff")); + assert(expr7->evaluate(fixture)); + + auto expr8 = make_expr(&test_fixture::some_const_string, &op::greater_than, std::string("abcdefghijklmnopqrstuvwxyz")); + assert(expr8->evaluate(fixture)); + + auto expr9 = make_expr(&test_fixture::some_const_string, &op::greater_than, std::string("this IS 4 T3s7 $tRing ")); + assert(!expr9->evaluate(fixture)); + + auto expr10 = make_expr(&test_fixture::some_const_string, &op::greater_than, fixture.some_const_string); + assert(!expr10->evaluate(fixture)); + } + } + + // test non-const string + { + fixture.some_string = "hello world!"; + + auto node = make_expr(&test_fixture::some_string, &op::equals, std::string("hello world!")); + assert(node->evaluate(fixture)); + + fixture.some_string = "hey, world!"; + assert(!node->evaluate(fixture)); + } } void test_char_ptr_evaluation() { From 346bf9de9bfe75ab39c9fcce531d1b2e6315e582 Mon Sep 17 00:00:00 2001 From: Noah Attwood Date: Tue, 9 May 2023 15:34:44 -0300 Subject: [PATCH 08/14] adds new expression evaluation unit tests for strings --- tests/expression_tree_leaf_node.cpp | 118 ++++++++++++++++++++++++++-- 1 file changed, 113 insertions(+), 5 deletions(-) diff --git a/tests/expression_tree_leaf_node.cpp b/tests/expression_tree_leaf_node.cpp index b4a974c..20d473e 100644 --- a/tests/expression_tree_leaf_node.cpp +++ b/tests/expression_tree_leaf_node.cpp @@ -148,13 +148,121 @@ void test_string_evaluation() { // test non-const string { - fixture.some_string = "hello world!"; + // test equals + { + auto expr = make_expr(&test_fixture::some_string, &op::equals, std::string("hello world!")); + + fixture.some_string = "hello world!"; + assert(expr->evaluate(fixture)); + + fixture.some_string = "hey, world!"; + assert(!expr->evaluate(fixture)); + + fixture.some_string = "1234"; + assert(!expr->evaluate(fixture)); + + fixture.some_string = "-----"; + assert(!expr->evaluate(fixture)); + + fixture.some_string = ""; + assert(!expr->evaluate(fixture)); + + fixture.some_string = " "; + assert(!expr->evaluate(fixture)); + } + + // test not_equals + { + auto expr = make_expr(&test_fixture::some_string, &op::not_equals, std::string("hello world!")); + + fixture.some_string = "hello world!"; + assert(!expr->evaluate(fixture)); + + fixture.some_string = "hey, world!"; + assert(expr->evaluate(fixture)); + + fixture.some_string = "1234"; + assert(expr->evaluate(fixture)); + + fixture.some_string = "-----"; + assert(expr->evaluate(fixture)); + + fixture.some_string = ""; + assert(expr->evaluate(fixture)); + + fixture.some_string = " "; + assert(expr->evaluate(fixture)); + } + + // test less_than + { + auto expr = make_expr(&test_fixture::some_string, &op::less_than, std::string("this IS 4 T3s7 $tRing ")); + + fixture.some_string = "u"; + assert(!expr->evaluate(fixture)); - auto node = make_expr(&test_fixture::some_string, &op::equals, std::string("hello world!")); - assert(node->evaluate(fixture)); + fixture.some_string = "z"; + assert(!expr->evaluate(fixture)); - fixture.some_string = "hey, world!"; - assert(!node->evaluate(fixture)); + fixture.some_string = "this IS 4 T3s7 $tRing "; + assert(!expr->evaluate(fixture)); + + fixture.some_string = ""; + assert(expr->evaluate(fixture)); + + fixture.some_string = "1234567890"; + assert(expr->evaluate(fixture)); + + fixture.some_string = "A"; + assert(expr->evaluate(fixture)); + + fixture.some_string = "this "; + assert(expr->evaluate(fixture)); + + fixture.some_string = "stuff"; + assert(expr->evaluate(fixture)); + + fixture.some_string = "abcdefghijklmnopqrstuvwxyz"; + assert(expr->evaluate(fixture)); + + fixture.some_string = "this IS 4 T3s7 $tRing "; + assert(!expr->evaluate(fixture)); + } + + // test greater_than + { + auto expr = make_expr(&test_fixture::some_string, &op::greater_than, std::string("this IS 4 T3s7 $tRing ")); + + fixture.some_string = "u"; + assert(expr->evaluate(fixture)); + + fixture.some_string = "z"; + assert(expr->evaluate(fixture)); + + fixture.some_string = "this IS 4 T3s7 $tRing "; + assert(expr->evaluate(fixture)); + + fixture.some_string = ""; + assert(!expr->evaluate(fixture)); + + fixture.some_string = "1234567890"; + assert(!expr->evaluate(fixture)); + + fixture.some_string = "A"; + assert(!expr->evaluate(fixture)); + + fixture.some_string = "this "; + assert(!expr->evaluate(fixture)); + + fixture.some_string = "stuff"; + assert(!expr->evaluate(fixture)); + + fixture.some_string = "abcdefghijklmnopqrstuvwxyz"; + assert(!expr->evaluate(fixture)); + + fixture.some_string = "this IS 4 T3s7 $tRing "; + assert(!expr->evaluate(fixture)); + } } } From ee4d30c7ec0806b6c4a67c86c8e3fae8acaa66ff Mon Sep 17 00:00:00 2001 From: Noah Attwood Date: Tue, 9 May 2023 16:14:20 -0300 Subject: [PATCH 09/14] adds unit tests for const functions and uints --- include/attwoodn/expression_tree.hpp | 14 ++ tests/expression_tree_leaf_node.cpp | 230 +++++++++++++++++++++++++++ 2 files changed, 244 insertions(+) diff --git a/include/attwoodn/expression_tree.hpp b/include/attwoodn/expression_tree.hpp index 45c1b12..01d1e51 100644 --- a/include/attwoodn/expression_tree.hpp +++ b/include/attwoodn/expression_tree.hpp @@ -218,14 +218,28 @@ namespace attwoodn::expression_tree { template struct type_id{typedef T type;}; + /** + * Makes an expression tree leaf node for comparing pointer-type member variables of a class/struct + */ template::type, typename std::enable_if::value, int>::type = 0> node::expression_tree_leaf_node* make_expr( CompValue Obj::* member_var, Op op, CompValue comp_value ) { return new node::expression_tree_leaf_node( member_var, op, comp_value ); } + /** + * Makes an expression tree leaf node for comparing value-type member variables of a class/struct + */ template::type> node::expression_tree_leaf_node* make_expr( const CompValue Obj::* member_var, Op op, CompValue comp_value ) { return new node::expression_tree_leaf_node( member_var, op, comp_value ); } + + /** + * Makes an expression tree leaf node for comparing a class/struct's const member function return values + */ + template::type> + node::expression_tree_leaf_node* make_expr( CompValue (Obj::* member_func)() const, Op op, CompValue comp_value ) { + return new node::expression_tree_leaf_node( member_func, op, comp_value ); + } } \ No newline at end of file diff --git a/tests/expression_tree_leaf_node.cpp b/tests/expression_tree_leaf_node.cpp index 20d473e..694a03f 100644 --- a/tests/expression_tree_leaf_node.cpp +++ b/tests/expression_tree_leaf_node.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -8,14 +9,23 @@ struct test_fixture { std::string some_string; const std::string some_const_string = "this IS 4 T3s7 $tRing "; char* some_char_ptr; + uint16_t some_uint; + + bool is_some_uint_greater_than_zero() const { + return some_uint; + } }; void test_string_evaluation(); void test_char_ptr_evaluation(); +void test_uint_evaluation(); +void test_const_func_evaluation(); int main(int argc, char** argv) { test_string_evaluation(); test_char_ptr_evaluation(); + test_uint_evaluation(); + test_const_func_evaluation(); return EXIT_SUCCESS; } @@ -371,4 +381,224 @@ void test_char_ptr_evaluation() { fixture.some_char_ptr = &c_too; assert(expr->evaluate(fixture)); } +} + +void test_uint_evaluation() { + test_fixture fixture; + + // test equals + { + auto expr = make_expr(&test_fixture::some_uint, op::equals, (uint16_t) 9001); + + fixture.some_uint = 0; + assert(!expr->evaluate(fixture)); + + fixture.some_uint = 1; + assert(!expr->evaluate(fixture)); + + fixture.some_uint = -1; + assert(!expr->evaluate(fixture)); + + fixture.some_uint = 9000; + assert(!expr->evaluate(fixture)); + + fixture.some_uint = 9002; + assert(!expr->evaluate(fixture)); + + fixture.some_uint = 9001; + assert(expr->evaluate(fixture)); + } + + // test not_equals + { + auto expr = make_expr(&test_fixture::some_uint, op::not_equals, (uint16_t) 9001); + + fixture.some_uint = 0; + assert(expr->evaluate(fixture)); + + fixture.some_uint = 1; + assert(expr->evaluate(fixture)); + + fixture.some_uint = -1; + assert(expr->evaluate(fixture)); + + fixture.some_uint = 9000; + assert(expr->evaluate(fixture)); + + fixture.some_uint = 9002; + assert(expr->evaluate(fixture)); + + fixture.some_uint = 9001; + assert(!expr->evaluate(fixture)); + } + + // test less_than + { + auto expr = make_expr(&test_fixture::some_uint, op::less_than, (uint16_t) 9001); + + fixture.some_uint = 0; + assert(expr->evaluate(fixture)); + + fixture.some_uint = 1; + assert(expr->evaluate(fixture)); + + fixture.some_uint = -1; + assert(!expr->evaluate(fixture)); + + fixture.some_uint = 9000; + assert(expr->evaluate(fixture)); + + fixture.some_uint = 9002; + assert(!expr->evaluate(fixture)); + + fixture.some_uint = 9001; + assert(!expr->evaluate(fixture)); + } + + // test greater_than + { + auto expr = make_expr(&test_fixture::some_uint, op::greater_than, (uint16_t) 9001); + + fixture.some_uint = 0; + assert(!expr->evaluate(fixture)); + + fixture.some_uint = 1; + assert(!expr->evaluate(fixture)); + + fixture.some_uint = -1; + assert(expr->evaluate(fixture)); + + fixture.some_uint = 9000; + assert(!expr->evaluate(fixture)); + + fixture.some_uint = 9002; + assert(expr->evaluate(fixture)); + + fixture.some_uint = 9001; + assert(!expr->evaluate(fixture)); + } +} + +void test_const_func_evaluation() { + test_fixture fixture; + + // test equals + { + auto expr1 = make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::equals, true); + auto expr2 = make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::equals, false); + + fixture.some_uint = 0; + assert(!expr1->evaluate(fixture)); + assert(expr2->evaluate(fixture)); + + fixture.some_uint = std::numeric_limits::min(); + assert(!expr1->evaluate(fixture)); + assert(expr2->evaluate(fixture)); + + fixture.some_uint = 1; + assert(expr1->evaluate(fixture)); + assert(!expr2->evaluate(fixture)); + + fixture.some_uint = 500; + assert(expr1->evaluate(fixture)); + assert(!expr2->evaluate(fixture)); + + fixture.some_uint = std::numeric_limits::max(); + assert(expr1->evaluate(fixture)); + assert(!expr2->evaluate(fixture)); + + fixture.some_uint = -1; + assert(expr1->evaluate(fixture)); + assert(!expr2->evaluate(fixture)); + } + + // test not_equals + { + auto expr1 = make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::not_equals, true); + auto expr2 = make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::not_equals, false); + + fixture.some_uint = 0; + assert(expr1->evaluate(fixture)); + assert(!expr2->evaluate(fixture)); + + fixture.some_uint = std::numeric_limits::min(); + assert(expr1->evaluate(fixture)); + assert(!expr2->evaluate(fixture)); + + fixture.some_uint = 1; + assert(!expr1->evaluate(fixture)); + assert(expr2->evaluate(fixture)); + + fixture.some_uint = 500; + assert(!expr1->evaluate(fixture)); + assert(expr2->evaluate(fixture)); + + fixture.some_uint = std::numeric_limits::max(); + assert(!expr1->evaluate(fixture)); + assert(expr2->evaluate(fixture)); + + fixture.some_uint = -1; + assert(!expr1->evaluate(fixture)); + assert(expr2->evaluate(fixture)); + } + + // test less_than + { + auto expr1 = make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::less_than, true); + auto expr2 = make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::less_than, false); + + fixture.some_uint = 0; + assert(expr1->evaluate(fixture)); + assert(!expr2->evaluate(fixture)); + + fixture.some_uint = std::numeric_limits::min(); + assert(expr1->evaluate(fixture)); + assert(!expr2->evaluate(fixture)); + + fixture.some_uint = 1; + assert(!expr1->evaluate(fixture)); + assert(!expr2->evaluate(fixture)); + + fixture.some_uint = 500; + assert(!expr1->evaluate(fixture)); + assert(!expr2->evaluate(fixture)); + + fixture.some_uint = std::numeric_limits::max(); + assert(!expr1->evaluate(fixture)); + assert(!expr2->evaluate(fixture)); + + fixture.some_uint = -1; + assert(!expr1->evaluate(fixture)); + assert(!expr2->evaluate(fixture)); + } + + // test greater_than + { + auto expr1 = make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::greater_than, true); + auto expr2 = make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::greater_than, false); + + fixture.some_uint = 0; + assert(!expr1->evaluate(fixture)); + assert(!expr2->evaluate(fixture)); + + fixture.some_uint = std::numeric_limits::min(); + assert(!expr1->evaluate(fixture)); + assert(!expr2->evaluate(fixture)); + + fixture.some_uint = 1; + assert(!expr1->evaluate(fixture)); + assert(expr2->evaluate(fixture)); + + fixture.some_uint = 500; + assert(!expr1->evaluate(fixture)); + assert(expr2->evaluate(fixture)); + + fixture.some_uint = std::numeric_limits::max(); + assert(!expr1->evaluate(fixture)); + assert(expr2->evaluate(fixture)); + + fixture.some_uint = -1; + assert(!expr1->evaluate(fixture)); + assert(expr2->evaluate(fixture)); + } } \ No newline at end of file From 01bc0171b903dbcf88fc2b27d0bf9a997d021bbc Mon Sep 17 00:00:00 2001 From: Noah Attwood Date: Tue, 9 May 2023 17:19:21 -0300 Subject: [PATCH 10/14] implements new unit tests for expression_tree_op_node class --- tests/CMakeLists.txt | 9 +++- tests/expression_tree_leaf_node.cpp | 12 +----- tests/expression_tree_op_node.cpp | 66 +++++++++++++++++++++++++++++ tests/test_utils.hpp | 28 ++++++++++++ 4 files changed, 102 insertions(+), 13 deletions(-) create mode 100644 tests/expression_tree_op_node.cpp create mode 100644 tests/test_utils.hpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 51a7ff8..f07b14a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,7 +1,12 @@ if(BUILD_TESTING) + + add_executable( operators_test operators.cpp ) + add_test( operators_test ${EXECUTABLE_OUTPUT_PATH}/operators_test ) + add_executable( expression_tree_leaf_node_test expression_tree_leaf_node.cpp ) add_test( expression_tree_leaf_node_test ${EXECUTABLE_OUTPUT_PATH}/expression_tree_leaf_node_test ) - add_executable( operators_test operators.cpp ) - add_test( operators_test ${EXECUTABLE_OUTPUT_PATH}/operators_test ) + add_executable( expression_tree_op_node_test expression_tree_op_node.cpp ) + add_test( expression_tree_op_node_test ${EXECUTABLE_OUTPUT_PATH}/expression_tree_op_node_test ) + endif() \ No newline at end of file diff --git a/tests/expression_tree_leaf_node.cpp b/tests/expression_tree_leaf_node.cpp index 694a03f..1eb2669 100644 --- a/tests/expression_tree_leaf_node.cpp +++ b/tests/expression_tree_leaf_node.cpp @@ -1,21 +1,11 @@ #include +#include "test_utils.hpp" #include #include #include using namespace attwoodn::expression_tree; -struct test_fixture { - std::string some_string; - const std::string some_const_string = "this IS 4 T3s7 $tRing "; - char* some_char_ptr; - uint16_t some_uint; - - bool is_some_uint_greater_than_zero() const { - return some_uint; - } -}; - void test_string_evaluation(); void test_char_ptr_evaluation(); void test_uint_evaluation(); diff --git a/tests/expression_tree_op_node.cpp b/tests/expression_tree_op_node.cpp new file mode 100644 index 0000000..c53261d --- /dev/null +++ b/tests/expression_tree_op_node.cpp @@ -0,0 +1,66 @@ +#include +#include "test_utils.hpp" +#include +#include +#include + +using namespace attwoodn::expression_tree; + +void test_one_layer_op_node_evaluation(); + +int main(int argc, char** argv) { + test_one_layer_op_node_evaluation(); + + return EXIT_SUCCESS; +} + +void test_one_layer_op_node_evaluation() { + auto child_expr1 = make_expr(&test_fixture::some_string, op::equals, std::string("hello, world!")); + auto child_expr2 = make_expr(&test_fixture::some_uint, op::less_than, (uint16_t) 500); + + auto expr = make_op_node(child_expr1, boolean_op::AND, child_expr2); + + test_fixture fixture; + fixture.some_string = "hello, world!"; + fixture.some_uint = 499; + assert(expr->evaluate(fixture)); + + fixture.some_uint = 0; + assert(expr->evaluate(fixture)); + + fixture.some_uint = 1; + assert(expr->evaluate(fixture)); + + fixture.some_uint = 250; + assert(expr->evaluate(fixture)); + + fixture.some_uint = 435; + assert(expr->evaluate(fixture)); + + fixture.some_uint = 500; + assert(!expr->evaluate(fixture)); + + fixture.some_uint = 501; + assert(!expr->evaluate(fixture)); + + fixture.some_uint = 9999; + assert(!expr->evaluate(fixture)); + + fixture.some_uint = std::numeric_limits::max(); + assert(!expr->evaluate(fixture)); + + fixture.some_uint = 499; + assert(expr->evaluate(fixture)); + + fixture.some_string = "hello!"; + assert(!expr->evaluate(fixture)); + + fixture.some_string = "hello world!"; + assert(!expr->evaluate(fixture)); + + fixture.some_string = "hello,world!"; + assert(!expr->evaluate(fixture)); + + fixture.some_string = "hello, world!"; + assert(expr->evaluate(fixture)); +} \ No newline at end of file diff --git a/tests/test_utils.hpp b/tests/test_utils.hpp new file mode 100644 index 0000000..16f5618 --- /dev/null +++ b/tests/test_utils.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +namespace et = attwoodn::expression_tree; + +struct test_fixture { + std::string some_string; + const std::string some_const_string = "this IS 4 T3s7 $tRing "; + char* some_char_ptr; + uint16_t some_uint; + + bool is_some_uint_greater_than_zero() const { + return some_uint; + } +}; + +/** + * Helper function for creating inner expression tree nodes. I'm undecided if this should be included in the public API +*/ +template +et::node::expression_tree_op_node* make_op_node(LeftChild* left, et::boolean_op op, RightChild* right) { + auto node = new et::node::expression_tree_op_node(op); + node->set_left(left); + node->set_right(right); + return node; +} \ No newline at end of file From a80d0a528958d84d942f9458f2a9ea236ef909c5 Mon Sep 17 00:00:00 2001 From: Noah Attwood Date: Tue, 9 May 2023 17:34:44 -0300 Subject: [PATCH 11/14] splits expression_tree_op_node unit tests into AND and OR test cases --- tests/expression_tree_op_node.cpp | 83 +++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 4 deletions(-) diff --git a/tests/expression_tree_op_node.cpp b/tests/expression_tree_op_node.cpp index c53261d..0afc8c9 100644 --- a/tests/expression_tree_op_node.cpp +++ b/tests/expression_tree_op_node.cpp @@ -6,15 +6,17 @@ using namespace attwoodn::expression_tree; -void test_one_layer_op_node_evaluation(); +void test_AND_op_node_evaluation(); +void test_OR_op_node_evaluation(); int main(int argc, char** argv) { - test_one_layer_op_node_evaluation(); + test_AND_op_node_evaluation(); + test_OR_op_node_evaluation(); return EXIT_SUCCESS; } -void test_one_layer_op_node_evaluation() { +void test_AND_op_node_evaluation() { auto child_expr1 = make_expr(&test_fixture::some_string, op::equals, std::string("hello, world!")); auto child_expr2 = make_expr(&test_fixture::some_uint, op::less_than, (uint16_t) 500); @@ -63,4 +65,77 @@ void test_one_layer_op_node_evaluation() { fixture.some_string = "hello, world!"; assert(expr->evaluate(fixture)); -} \ No newline at end of file +} + +void test_OR_op_node_evaluation() { + auto child_expr1 = make_expr(&test_fixture::some_string, op::equals, std::string("hello, world!")); + auto child_expr2 = make_expr(&test_fixture::some_uint, op::less_than, (uint16_t) 500); + + auto expr = make_op_node(child_expr1, boolean_op::OR, child_expr2); + + test_fixture fixture; + fixture.some_string = "hello, world!"; + fixture.some_uint = 499; + assert(expr->evaluate(fixture)); + + fixture.some_uint = 0; + assert(expr->evaluate(fixture)); + + fixture.some_uint = 1; + assert(expr->evaluate(fixture)); + + fixture.some_uint = 250; + assert(expr->evaluate(fixture)); + + fixture.some_uint = 435; + assert(expr->evaluate(fixture)); + + fixture.some_uint = 500; + assert(expr->evaluate(fixture)); + + fixture.some_uint = 501; + assert(expr->evaluate(fixture)); + + fixture.some_uint = 9999; + assert(expr->evaluate(fixture)); + + fixture.some_uint = std::numeric_limits::max(); + assert(expr->evaluate(fixture)); + + fixture.some_uint = 499; + assert(expr->evaluate(fixture)); + + fixture.some_string = "hello!"; + assert(expr->evaluate(fixture)); + + fixture.some_string = "hello world!"; + assert(expr->evaluate(fixture)); + + fixture.some_string = "hello,world!"; + assert(expr->evaluate(fixture)); + + fixture.some_string = "some test string"; + fixture.some_uint = 501; + assert(!expr->evaluate(fixture)); + + fixture.some_string = "hello!"; + fixture.some_uint = 5876; + assert(!expr->evaluate(fixture)); + + fixture.some_string = "hello hello HELLO"; + fixture.some_uint = 12345; + assert(!expr->evaluate(fixture)); + + fixture.some_string = ""; + fixture.some_uint = -1; + assert(!expr->evaluate(fixture)); + + fixture.some_string = ""; + fixture.some_uint = 0; + assert(expr->evaluate(fixture)); + + fixture.some_string = "hello, world!"; + fixture.some_uint = 350; + assert(expr->evaluate(fixture)); +} + From 785f1cee8825eb6bfd98cbf347c6a000f994b0e4 Mon Sep 17 00:00:00 2001 From: Noah Attwood Date: Tue, 9 May 2023 18:24:52 -0300 Subject: [PATCH 12/14] adds static analyzers for detecting memory leaks in the unit tests, and fixes existing memory leaks with raw pointers by wrapping them in unique_ptrs --- CMakeLists.txt | 2 +- tests/CMakeLists.txt | 8 +- tests/expression_tree_leaf_node.cpp | 208 ++++++++++++++++++++-------- tests/expression_tree_op_node.cpp | 1 + tests/test_utils.hpp | 7 +- 5 files changed, 165 insertions(+), 61 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7e0836b..52c239a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ project(expression_tree include(CTest) if (NOT DEFINED CMAKE_CXX_STANDARD) - set(CMAKE_CXX_STANDARD 11) + set(CMAKE_CXX_STANDARD 14) endif() if (NOT CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE STREQUAL "") diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f07b14a..b692197 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,12 +1,18 @@ if(BUILD_TESTING) - + add_executable( operators_test operators.cpp ) + target_link_libraries( operators_test "-fsanitize=address" ) + target_compile_options( operators_test PRIVATE -fsanitize=address ) add_test( operators_test ${EXECUTABLE_OUTPUT_PATH}/operators_test ) add_executable( expression_tree_leaf_node_test expression_tree_leaf_node.cpp ) + target_link_libraries( expression_tree_leaf_node_test "-fsanitize=address" ) + target_compile_options( expression_tree_leaf_node_test PRIVATE -fsanitize=address ) add_test( expression_tree_leaf_node_test ${EXECUTABLE_OUTPUT_PATH}/expression_tree_leaf_node_test ) add_executable( expression_tree_op_node_test expression_tree_op_node.cpp ) + target_link_libraries( expression_tree_op_node_test "-fsanitize=address" ) + target_compile_options( expression_tree_op_node_test PRIVATE -fsanitize=address ) add_test( expression_tree_op_node_test ${EXECUTABLE_OUTPUT_PATH}/expression_tree_op_node_test ) endif() \ No newline at end of file diff --git a/tests/expression_tree_leaf_node.cpp b/tests/expression_tree_leaf_node.cpp index 1eb2669..01d4c6a 100644 --- a/tests/expression_tree_leaf_node.cpp +++ b/tests/expression_tree_leaf_node.cpp @@ -27,121 +27,193 @@ void test_string_evaluation() { { // test equals { - auto expr1 = make_expr(&test_fixture::some_const_string, &op::equals, std::string("hello world!")); + auto expr1 = std::unique_ptr>( + make_expr(&test_fixture::some_const_string, &op::equals, std::string("hello world!")) + ); assert(!expr1->evaluate(fixture)); - auto expr2 = make_expr(&test_fixture::some_const_string, &op::equals, std::string("h")); + auto expr2 = std::unique_ptr>( + make_expr(&test_fixture::some_const_string, &op::equals, std::string("h")) + ); assert(!expr2->evaluate(fixture)); - auto expr3 = make_expr(&test_fixture::some_const_string, &op::equals, std::string("t")); + auto expr3 = std::unique_ptr>( + make_expr(&test_fixture::some_const_string, &op::equals, std::string("t")) + ); assert(!expr3->evaluate(fixture)); - auto expr4 = make_expr(&test_fixture::some_const_string, &op::equals, std::string("this ")); + auto expr4 = std::unique_ptr>( + make_expr(&test_fixture::some_const_string, &op::equals, std::string("this ")) + ); assert(!expr4->evaluate(fixture)); - auto expr5 = make_expr(&test_fixture::some_const_string, &op::equals, std::string("this IS 4 T3s7 $tRing ")); + auto expr5 = std::unique_ptr>( + make_expr(&test_fixture::some_const_string, &op::equals, std::string("this IS 4 T3s7 $tRing ")) + ); assert(!expr5->evaluate(fixture)); - auto expr6 = make_expr(&test_fixture::some_const_string, &op::equals, std::string("this IS 4 T3s7 $tRing")); + auto expr6 = std::unique_ptr>( + make_expr(&test_fixture::some_const_string, &op::equals, std::string("this IS 4 T3s7 $tRing")) + ); assert(!expr6->evaluate(fixture)); - auto expr7 = make_expr(&test_fixture::some_const_string, &op::equals, std::string("this IS 4 T3s7 $tRing ")); + auto expr7 = std::unique_ptr>( + make_expr(&test_fixture::some_const_string, &op::equals, std::string("this IS 4 T3s7 $tRing ")) + ); assert(expr7->evaluate(fixture)); - auto expr8 = make_expr(&test_fixture::some_const_string, &op::equals, fixture.some_const_string); + auto expr8 = std::unique_ptr>( + make_expr(&test_fixture::some_const_string, &op::equals, fixture.some_const_string) + ); assert(expr8->evaluate(fixture)); } // test not_equals { - auto expr1 = make_expr(&test_fixture::some_const_string, &op::not_equals, std::string("hello world!")); + auto expr1 = std::unique_ptr>( + make_expr(&test_fixture::some_const_string, &op::not_equals, std::string("hello world!")) + ); assert(expr1->evaluate(fixture)); - auto expr2 = make_expr(&test_fixture::some_const_string, &op::not_equals, std::string("h")); + auto expr2 = std::unique_ptr>( + make_expr(&test_fixture::some_const_string, &op::not_equals, std::string("h")) + ); assert(expr2->evaluate(fixture)); - auto expr3 = make_expr(&test_fixture::some_const_string, &op::not_equals, std::string("t")); + auto expr3 = std::unique_ptr>( + make_expr(&test_fixture::some_const_string, &op::not_equals, std::string("t")) + ); assert(expr3->evaluate(fixture)); - auto expr4 = make_expr(&test_fixture::some_const_string, &op::not_equals, std::string("this ")); + auto expr4 = std::unique_ptr>( + make_expr(&test_fixture::some_const_string, &op::not_equals, std::string("this ")) + ); assert(expr4->evaluate(fixture)); - auto expr5 = make_expr(&test_fixture::some_const_string, &op::not_equals, std::string("this IS 4 T3s7 $tRing ")); + auto expr5 = std::unique_ptr>( + make_expr(&test_fixture::some_const_string, &op::not_equals, std::string("this IS 4 T3s7 $tRing ")) + ); assert(expr5->evaluate(fixture)); - auto expr6 = make_expr(&test_fixture::some_const_string, &op::not_equals, std::string("this IS 4 T3s7 $tRing")); + auto expr6 = std::unique_ptr>( + make_expr(&test_fixture::some_const_string, &op::not_equals, std::string("this IS 4 T3s7 $tRing")) + ); assert(expr6->evaluate(fixture)); - auto expr7 = make_expr(&test_fixture::some_const_string, &op::not_equals, std::string("this IS 4 T3s7 $tRing ")); + auto expr7 = std::unique_ptr>( + make_expr(&test_fixture::some_const_string, &op::not_equals, std::string("this IS 4 T3s7 $tRing ")) + ); assert(!expr7->evaluate(fixture)); - auto expr8 = make_expr(&test_fixture::some_const_string, &op::not_equals, fixture.some_const_string); + auto expr8 = std::unique_ptr>( + make_expr(&test_fixture::some_const_string, &op::not_equals, fixture.some_const_string) + ); assert(!expr8->evaluate(fixture)); } // test less_than { - auto expr1 = make_expr(&test_fixture::some_const_string, &op::less_than, std::string("u")); + auto expr1 = std::unique_ptr>( + make_expr(&test_fixture::some_const_string, &op::less_than, std::string("u")) + ); assert(expr1->evaluate(fixture)); - auto expr2 = make_expr(&test_fixture::some_const_string, &op::less_than, std::string("z")); + auto expr2 = std::unique_ptr>( + make_expr(&test_fixture::some_const_string, &op::less_than, std::string("z")) + ); assert(expr2->evaluate(fixture)); - auto expr3 = make_expr(&test_fixture::some_const_string, &op::less_than, std::string("this IS 4 T3s7 $tRing ")); + auto expr3 = std::unique_ptr>( + make_expr(&test_fixture::some_const_string, &op::less_than, std::string("this IS 4 T3s7 $tRing ")) + ); assert(expr3->evaluate(fixture)); - auto expr4 = make_expr(&test_fixture::some_const_string, &op::less_than, std::string("1234567890")); + auto expr4 = std::unique_ptr>( + make_expr(&test_fixture::some_const_string, &op::less_than, std::string("1234567890")) + ); assert(!expr4->evaluate(fixture)); - auto expr5 = make_expr(&test_fixture::some_const_string, &op::less_than, std::string("A")); + auto expr5 = std::unique_ptr>( + make_expr(&test_fixture::some_const_string, &op::less_than, std::string("A")) + ); assert(!expr5->evaluate(fixture)); - auto expr6 = make_expr(&test_fixture::some_const_string, &op::less_than, std::string("this ")); + auto expr6 = std::unique_ptr>( + make_expr(&test_fixture::some_const_string, &op::less_than, std::string("this ")) + ); assert(!expr6->evaluate(fixture)); - auto expr7 = make_expr(&test_fixture::some_const_string, &op::less_than, std::string("stuff")); + auto expr7 = std::unique_ptr>( + make_expr(&test_fixture::some_const_string, &op::less_than, std::string("stuff")) + ); assert(!expr7->evaluate(fixture)); - auto expr8 = make_expr(&test_fixture::some_const_string, &op::less_than, std::string("abcdefghijklmnopqrstuvwxyz")); + auto expr8 = std::unique_ptr>( + make_expr(&test_fixture::some_const_string, &op::less_than, std::string("abcdefghijklmnopqrstuvwxyz")) + ); assert(!expr8->evaluate(fixture)); - auto expr9 = make_expr(&test_fixture::some_const_string, &op::less_than, std::string("this IS 4 T3s7 $tRing ")); + auto expr9 = std::unique_ptr>( + make_expr(&test_fixture::some_const_string, &op::less_than, std::string("this IS 4 T3s7 $tRing ")) + ); assert(!expr9->evaluate(fixture)); - auto expr10 = make_expr(&test_fixture::some_const_string, &op::less_than, fixture.some_const_string); + auto expr10 = std::unique_ptr>( + make_expr(&test_fixture::some_const_string, &op::less_than, fixture.some_const_string) + ); assert(!expr10->evaluate(fixture)); } // test greater_than { - auto expr1 = make_expr(&test_fixture::some_const_string, &op::greater_than, std::string("u")); + auto expr1 = std::unique_ptr>( + make_expr(&test_fixture::some_const_string, &op::greater_than, std::string("u")) + ); assert(!expr1->evaluate(fixture)); - auto expr2 = make_expr(&test_fixture::some_const_string, &op::greater_than, std::string("z")); + auto expr2 = std::unique_ptr>( + make_expr(&test_fixture::some_const_string, &op::greater_than, std::string("z")) + ); assert(!expr2->evaluate(fixture)); - auto expr3 = make_expr(&test_fixture::some_const_string, &op::greater_than, std::string("this IS 4 T3s7 $tRing ")); + auto expr3 = std::unique_ptr>( + make_expr(&test_fixture::some_const_string, &op::greater_than, std::string("this IS 4 T3s7 $tRing ")) + ); assert(!expr3->evaluate(fixture)); - auto expr4 = make_expr(&test_fixture::some_const_string, &op::greater_than, std::string("1234567890")); + auto expr4 = std::unique_ptr>( + make_expr(&test_fixture::some_const_string, &op::greater_than, std::string("1234567890")) + ); assert(expr4->evaluate(fixture)); - auto expr5 = make_expr(&test_fixture::some_const_string, &op::greater_than, std::string("A")); + auto expr5 = std::unique_ptr>( + make_expr(&test_fixture::some_const_string, &op::greater_than, std::string("A")) + ); assert(expr5->evaluate(fixture)); - auto expr6 = make_expr(&test_fixture::some_const_string, &op::greater_than, std::string("this ")); + auto expr6 = std::unique_ptr>( + make_expr(&test_fixture::some_const_string, &op::greater_than, std::string("this ")) + ); assert(expr6->evaluate(fixture)); - auto expr7 = make_expr(&test_fixture::some_const_string, &op::greater_than, std::string("stuff")); + auto expr7 = std::unique_ptr>( + make_expr(&test_fixture::some_const_string, &op::greater_than, std::string("stuff")) + ); assert(expr7->evaluate(fixture)); - auto expr8 = make_expr(&test_fixture::some_const_string, &op::greater_than, std::string("abcdefghijklmnopqrstuvwxyz")); + auto expr8 = std::unique_ptr>( + make_expr(&test_fixture::some_const_string, &op::greater_than, std::string("abcdefghijklmnopqrstuvwxyz")) + ); assert(expr8->evaluate(fixture)); - auto expr9 = make_expr(&test_fixture::some_const_string, &op::greater_than, std::string("this IS 4 T3s7 $tRing ")); + auto expr9 = std::unique_ptr>( + make_expr(&test_fixture::some_const_string, &op::greater_than, std::string("this IS 4 T3s7 $tRing ")) + ); assert(!expr9->evaluate(fixture)); - auto expr10 = make_expr(&test_fixture::some_const_string, &op::greater_than, fixture.some_const_string); + auto expr10 = std::unique_ptr>( + make_expr(&test_fixture::some_const_string, &op::greater_than, fixture.some_const_string) + ); assert(!expr10->evaluate(fixture)); } } @@ -150,7 +222,9 @@ void test_string_evaluation() { { // test equals { - auto expr = make_expr(&test_fixture::some_string, &op::equals, std::string("hello world!")); + auto expr = std::unique_ptr>( + make_expr(&test_fixture::some_string, &op::equals, std::string("hello world!")) + ); fixture.some_string = "hello world!"; assert(expr->evaluate(fixture)); @@ -173,7 +247,9 @@ void test_string_evaluation() { // test not_equals { - auto expr = make_expr(&test_fixture::some_string, &op::not_equals, std::string("hello world!")); + auto expr = std::unique_ptr>( + make_expr(&test_fixture::some_string, &op::not_equals, std::string("hello world!")) + ); fixture.some_string = "hello world!"; assert(!expr->evaluate(fixture)); @@ -196,7 +272,9 @@ void test_string_evaluation() { // test less_than { - auto expr = make_expr(&test_fixture::some_string, &op::less_than, std::string("this IS 4 T3s7 $tRing ")); + auto expr = std::unique_ptr>( + make_expr(&test_fixture::some_string, &op::less_than, std::string("this IS 4 T3s7 $tRing ")) + ); fixture.some_string = "u"; assert(!expr->evaluate(fixture)); @@ -231,7 +309,9 @@ void test_string_evaluation() { // test greater_than { - auto expr = make_expr(&test_fixture::some_string, &op::greater_than, std::string("this IS 4 T3s7 $tRing ")); + auto expr = std::unique_ptr>( + make_expr(&test_fixture::some_string, &op::greater_than, std::string("this IS 4 T3s7 $tRing ")) + ); fixture.some_string = "u"; assert(expr->evaluate(fixture)); @@ -279,7 +359,9 @@ void test_char_ptr_evaluation() { // test equals { // make an expression : given char* == 'a' - auto expr = make_expr(&test_fixture::some_char_ptr, &op::equals, &a); + auto expr = std::unique_ptr>( + make_expr(&test_fixture::some_char_ptr, &op::equals, &a) + ); fixture.some_char_ptr = &a; assert(expr->evaluate(fixture)); @@ -303,7 +385,9 @@ void test_char_ptr_evaluation() { // test not_equals { // make an expression : given char* != 'a' - auto expr = make_expr(&test_fixture::some_char_ptr, &op::not_equals, &a); + auto expr = std::unique_ptr>( + make_expr(&test_fixture::some_char_ptr, &op::not_equals, &a) + ); fixture.some_char_ptr = &a; assert(!expr->evaluate(fixture)); @@ -327,7 +411,9 @@ void test_char_ptr_evaluation() { // test less_than { // make an expression : given char* < 'b' - auto expr = make_expr(&test_fixture::some_char_ptr, &op::less_than, &b); + auto expr = std::unique_ptr>( + make_expr(&test_fixture::some_char_ptr, &op::less_than, &b) + ); fixture.some_char_ptr = &a; assert(expr->evaluate(fixture)); @@ -351,7 +437,9 @@ void test_char_ptr_evaluation() { // test greater_than { // make an expression : given char* > 'b' - auto expr = make_expr(&test_fixture::some_char_ptr, &op::greater_than, &b); + auto expr = std::unique_ptr>( + make_expr(&test_fixture::some_char_ptr, &op::greater_than, &b) + ); fixture.some_char_ptr = &a; assert(!expr->evaluate(fixture)); @@ -378,7 +466,9 @@ void test_uint_evaluation() { // test equals { - auto expr = make_expr(&test_fixture::some_uint, op::equals, (uint16_t) 9001); + auto expr = std::unique_ptr>( + make_expr(&test_fixture::some_uint, op::equals, (uint16_t) 9001) + ); fixture.some_uint = 0; assert(!expr->evaluate(fixture)); @@ -401,7 +491,9 @@ void test_uint_evaluation() { // test not_equals { - auto expr = make_expr(&test_fixture::some_uint, op::not_equals, (uint16_t) 9001); + auto expr = std::unique_ptr>( + make_expr(&test_fixture::some_uint, op::not_equals, (uint16_t) 9001) + ); fixture.some_uint = 0; assert(expr->evaluate(fixture)); @@ -424,7 +516,9 @@ void test_uint_evaluation() { // test less_than { - auto expr = make_expr(&test_fixture::some_uint, op::less_than, (uint16_t) 9001); + auto expr = std::unique_ptr>( + make_expr(&test_fixture::some_uint, op::less_than, (uint16_t) 9001) + ); fixture.some_uint = 0; assert(expr->evaluate(fixture)); @@ -447,7 +541,9 @@ void test_uint_evaluation() { // test greater_than { - auto expr = make_expr(&test_fixture::some_uint, op::greater_than, (uint16_t) 9001); + auto expr = std::unique_ptr>( + make_expr(&test_fixture::some_uint, op::greater_than, (uint16_t) 9001) + ); fixture.some_uint = 0; assert(!expr->evaluate(fixture)); @@ -474,8 +570,8 @@ void test_const_func_evaluation() { // test equals { - auto expr1 = make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::equals, true); - auto expr2 = make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::equals, false); + auto expr1 = std::unique_ptr>(make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::equals, true)); + auto expr2 = std::unique_ptr>(make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::equals, false)); fixture.some_uint = 0; assert(!expr1->evaluate(fixture)); @@ -504,8 +600,8 @@ void test_const_func_evaluation() { // test not_equals { - auto expr1 = make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::not_equals, true); - auto expr2 = make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::not_equals, false); + auto expr1 = std::unique_ptr>(make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::not_equals, true)); + auto expr2 = std::unique_ptr>(make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::not_equals, false)); fixture.some_uint = 0; assert(expr1->evaluate(fixture)); @@ -534,8 +630,8 @@ void test_const_func_evaluation() { // test less_than { - auto expr1 = make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::less_than, true); - auto expr2 = make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::less_than, false); + auto expr1 = std::unique_ptr>(make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::less_than, true)); + auto expr2 = std::unique_ptr>(make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::less_than, false)); fixture.some_uint = 0; assert(expr1->evaluate(fixture)); @@ -564,8 +660,8 @@ void test_const_func_evaluation() { // test greater_than { - auto expr1 = make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::greater_than, true); - auto expr2 = make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::greater_than, false); + auto expr1 = std::unique_ptr>(make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::greater_than, true)); + auto expr2 = std::unique_ptr>(make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::greater_than, false)); fixture.some_uint = 0; assert(!expr1->evaluate(fixture)); diff --git a/tests/expression_tree_op_node.cpp b/tests/expression_tree_op_node.cpp index 0afc8c9..66323e5 100644 --- a/tests/expression_tree_op_node.cpp +++ b/tests/expression_tree_op_node.cpp @@ -1,6 +1,7 @@ #include #include "test_utils.hpp" #include +#include #include #include diff --git a/tests/test_utils.hpp b/tests/test_utils.hpp index 16f5618..2ecfdac 100644 --- a/tests/test_utils.hpp +++ b/tests/test_utils.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include namespace et = attwoodn::expression_tree; @@ -17,12 +18,12 @@ struct test_fixture { }; /** - * Helper function for creating inner expression tree nodes. I'm undecided if this should be included in the public API + * Helper function for creating inner expression tree nodes. I'm undecided if this should be included in the public API. */ template -et::node::expression_tree_op_node* make_op_node(LeftChild* left, et::boolean_op op, RightChild* right) { +std::unique_ptr> make_op_node(LeftChild* left, et::boolean_op op, RightChild* right) { auto node = new et::node::expression_tree_op_node(op); node->set_left(left); node->set_right(right); - return node; + return std::unique_ptr>(node); } \ No newline at end of file From ef6d3d17a4d1d06ef758abffbb8e26fd33025599 Mon Sep 17 00:00:00 2001 From: Noah Attwood Date: Tue, 9 May 2023 21:29:52 -0300 Subject: [PATCH 13/14] renames expression_tree_node_base to simply expression_tree_node, implements AND/OR functions on expression_tree_leaf_node class --- include/attwoodn/expression_tree.hpp | 74 ++++++++++++++++-- tests/expression_tree_leaf_node.cpp | 112 +++++++++++++-------------- tests/test_utils.hpp | 4 +- 3 files changed, 125 insertions(+), 65 deletions(-) diff --git a/include/attwoodn/expression_tree.hpp b/include/attwoodn/expression_tree.hpp index 01d1e51..31bc9ea 100644 --- a/include/attwoodn/expression_tree.hpp +++ b/include/attwoodn/expression_tree.hpp @@ -70,9 +70,9 @@ namespace attwoodn::expression_tree { * @brief The base class representing all expression tree nodes. */ template - class expression_tree_node_base { + class expression_tree_node { public: - virtual ~expression_tree_node_base() {}; + virtual ~expression_tree_node() {}; /** * @brief Evaluates the given object to determine if it satisfies the expressions defined in this node and all child nodes. @@ -91,7 +91,7 @@ namespace attwoodn::expression_tree { * child node, as well as the boolean operation to be performed (e.g. left child AND right child, or left child OR right child). */ template - class expression_tree_op_node : public expression_tree_node_base { + class expression_tree_op_node : public expression_tree_node { public: using this_type = expression_tree_op_node; @@ -152,12 +152,12 @@ namespace attwoodn::expression_tree { }; /** - * @brief Represents leaf nodes of the tree. These nodes contain a reference to a member variable or member function of the - * given Obj type, the requested logical operation to be performed (e.g. equals, greater_than, etc.), and the + * @brief Represents leaf nodes of the tree. These nodes contain: a reference to a member variable or member function of the + * given Obj type; the requested logical operation to be performed (e.g. equals, greater_than, etc.); and the * value to compare to the given member variable or member function of an Obj instance. */ template - class expression_tree_leaf_node : public expression_tree_node_base { + class expression_tree_leaf_node : public expression_tree_node { public: using this_type = expression_tree_leaf_node; @@ -207,6 +207,66 @@ namespace attwoodn::expression_tree { return logical_op_(actual_value, comp_value_); } + /** + * Performs an AND operation with another expression_tree_leaf_node to create a heap-allocated pointer + * to a new expression_tree_op_node. The returned expression_tree_op_node becomes the parent of both this node + * and the other node that was AND'ed with this node. This node becomes the left child. The other node becomes + * the right child. + */ + template>::value>* = nullptr> + expression_tree_op_node* AND (OtherLeafNode* other) { + auto* op_node = new expression_tree_op_node(boolean_op::AND); + op_node->set_left(this); + op_node->set_right(other); + return op_node; + } + + /** + * Performs an OR operation with another expression_tree_leaf_node to create a heap-allocated pointer + * to a new expression_tree_op_node. The returned expression_tree_op_node becomes the parent of both this node + * and the other node that was OR'ed with this node. This node becomes the left child. The other node becomes + * the right child. + */ + template>::value>* = nullptr> + expression_tree_op_node* OR (OtherLeafNode* other) { + auto* op_node = new expression_tree_op_node(boolean_op::OR); + op_node->set_left(this); + op_node->set_right(other); + return op_node; + } + + /** + * Performs an AND operation with an expression_tree_op_node to create a heap-allocated pointer + * to a new expression_tree_op_node. The returned expression_tree_op_node becomes the parent of both this node + * and the other node that was AND'ed with this node. This node becomes the left child. The other node becomes + * the right child. + */ + template>::value>* = nullptr> + expression_tree_op_node* AND (OtherOpNode* other) { + auto* op_node = new expression_tree_op_node(boolean_op::AND); + op_node->set_left(this); + op_node->set_right(other); + return op_node; + } + + /** + * Performs an OR operation with an expression_tree_op_node to create a heap-allocated pointer + * to a new expression_tree_op_node. The returned expression_tree_op_node becomes the parent of both this node + * and the other node that was OR'ed with this node. This node becomes the left child. The other node becomes + * the right child. + */ + template>::value>* = nullptr> + expression_tree_op_node* OR (OtherOpNode* other) { + auto* op_node = new expression_tree_op_node(boolean_op::OR); + op_node->set_left(this); + op_node->set_right(other); + return op_node; + } + private: CompValue (Obj::* member_func_)() const = nullptr; const CompValue Obj::* member_var_ = nullptr; @@ -236,7 +296,7 @@ namespace attwoodn::expression_tree { } /** - * Makes an expression tree leaf node for comparing a class/struct's const member function return values + * Makes an expression tree leaf node for comparing the return value from a class/struct's const member function */ template::type> node::expression_tree_leaf_node* make_expr( CompValue (Obj::* member_func)() const, Op op, CompValue comp_value ) { diff --git a/tests/expression_tree_leaf_node.cpp b/tests/expression_tree_leaf_node.cpp index 01d4c6a..147a1b7 100644 --- a/tests/expression_tree_leaf_node.cpp +++ b/tests/expression_tree_leaf_node.cpp @@ -27,42 +27,42 @@ void test_string_evaluation() { { // test equals { - auto expr1 = std::unique_ptr>( + auto expr1 = std::unique_ptr>( make_expr(&test_fixture::some_const_string, &op::equals, std::string("hello world!")) ); assert(!expr1->evaluate(fixture)); - auto expr2 = std::unique_ptr>( + auto expr2 = std::unique_ptr>( make_expr(&test_fixture::some_const_string, &op::equals, std::string("h")) ); assert(!expr2->evaluate(fixture)); - auto expr3 = std::unique_ptr>( + auto expr3 = std::unique_ptr>( make_expr(&test_fixture::some_const_string, &op::equals, std::string("t")) ); assert(!expr3->evaluate(fixture)); - auto expr4 = std::unique_ptr>( + auto expr4 = std::unique_ptr>( make_expr(&test_fixture::some_const_string, &op::equals, std::string("this ")) ); assert(!expr4->evaluate(fixture)); - auto expr5 = std::unique_ptr>( + auto expr5 = std::unique_ptr>( make_expr(&test_fixture::some_const_string, &op::equals, std::string("this IS 4 T3s7 $tRing ")) ); assert(!expr5->evaluate(fixture)); - auto expr6 = std::unique_ptr>( + auto expr6 = std::unique_ptr>( make_expr(&test_fixture::some_const_string, &op::equals, std::string("this IS 4 T3s7 $tRing")) ); assert(!expr6->evaluate(fixture)); - auto expr7 = std::unique_ptr>( + auto expr7 = std::unique_ptr>( make_expr(&test_fixture::some_const_string, &op::equals, std::string("this IS 4 T3s7 $tRing ")) ); assert(expr7->evaluate(fixture)); - auto expr8 = std::unique_ptr>( + auto expr8 = std::unique_ptr>( make_expr(&test_fixture::some_const_string, &op::equals, fixture.some_const_string) ); assert(expr8->evaluate(fixture)); @@ -70,42 +70,42 @@ void test_string_evaluation() { // test not_equals { - auto expr1 = std::unique_ptr>( + auto expr1 = std::unique_ptr>( make_expr(&test_fixture::some_const_string, &op::not_equals, std::string("hello world!")) ); assert(expr1->evaluate(fixture)); - auto expr2 = std::unique_ptr>( + auto expr2 = std::unique_ptr>( make_expr(&test_fixture::some_const_string, &op::not_equals, std::string("h")) ); assert(expr2->evaluate(fixture)); - auto expr3 = std::unique_ptr>( + auto expr3 = std::unique_ptr>( make_expr(&test_fixture::some_const_string, &op::not_equals, std::string("t")) ); assert(expr3->evaluate(fixture)); - auto expr4 = std::unique_ptr>( + auto expr4 = std::unique_ptr>( make_expr(&test_fixture::some_const_string, &op::not_equals, std::string("this ")) ); assert(expr4->evaluate(fixture)); - auto expr5 = std::unique_ptr>( + auto expr5 = std::unique_ptr>( make_expr(&test_fixture::some_const_string, &op::not_equals, std::string("this IS 4 T3s7 $tRing ")) ); assert(expr5->evaluate(fixture)); - auto expr6 = std::unique_ptr>( + auto expr6 = std::unique_ptr>( make_expr(&test_fixture::some_const_string, &op::not_equals, std::string("this IS 4 T3s7 $tRing")) ); assert(expr6->evaluate(fixture)); - auto expr7 = std::unique_ptr>( + auto expr7 = std::unique_ptr>( make_expr(&test_fixture::some_const_string, &op::not_equals, std::string("this IS 4 T3s7 $tRing ")) ); assert(!expr7->evaluate(fixture)); - auto expr8 = std::unique_ptr>( + auto expr8 = std::unique_ptr>( make_expr(&test_fixture::some_const_string, &op::not_equals, fixture.some_const_string) ); assert(!expr8->evaluate(fixture)); @@ -113,52 +113,52 @@ void test_string_evaluation() { // test less_than { - auto expr1 = std::unique_ptr>( + auto expr1 = std::unique_ptr>( make_expr(&test_fixture::some_const_string, &op::less_than, std::string("u")) ); assert(expr1->evaluate(fixture)); - auto expr2 = std::unique_ptr>( + auto expr2 = std::unique_ptr>( make_expr(&test_fixture::some_const_string, &op::less_than, std::string("z")) ); assert(expr2->evaluate(fixture)); - auto expr3 = std::unique_ptr>( + auto expr3 = std::unique_ptr>( make_expr(&test_fixture::some_const_string, &op::less_than, std::string("this IS 4 T3s7 $tRing ")) ); assert(expr3->evaluate(fixture)); - auto expr4 = std::unique_ptr>( + auto expr4 = std::unique_ptr>( make_expr(&test_fixture::some_const_string, &op::less_than, std::string("1234567890")) ); assert(!expr4->evaluate(fixture)); - auto expr5 = std::unique_ptr>( + auto expr5 = std::unique_ptr>( make_expr(&test_fixture::some_const_string, &op::less_than, std::string("A")) ); assert(!expr5->evaluate(fixture)); - auto expr6 = std::unique_ptr>( + auto expr6 = std::unique_ptr>( make_expr(&test_fixture::some_const_string, &op::less_than, std::string("this ")) ); assert(!expr6->evaluate(fixture)); - auto expr7 = std::unique_ptr>( + auto expr7 = std::unique_ptr>( make_expr(&test_fixture::some_const_string, &op::less_than, std::string("stuff")) ); assert(!expr7->evaluate(fixture)); - auto expr8 = std::unique_ptr>( + auto expr8 = std::unique_ptr>( make_expr(&test_fixture::some_const_string, &op::less_than, std::string("abcdefghijklmnopqrstuvwxyz")) ); assert(!expr8->evaluate(fixture)); - auto expr9 = std::unique_ptr>( + auto expr9 = std::unique_ptr>( make_expr(&test_fixture::some_const_string, &op::less_than, std::string("this IS 4 T3s7 $tRing ")) ); assert(!expr9->evaluate(fixture)); - auto expr10 = std::unique_ptr>( + auto expr10 = std::unique_ptr>( make_expr(&test_fixture::some_const_string, &op::less_than, fixture.some_const_string) ); assert(!expr10->evaluate(fixture)); @@ -166,52 +166,52 @@ void test_string_evaluation() { // test greater_than { - auto expr1 = std::unique_ptr>( + auto expr1 = std::unique_ptr>( make_expr(&test_fixture::some_const_string, &op::greater_than, std::string("u")) ); assert(!expr1->evaluate(fixture)); - auto expr2 = std::unique_ptr>( + auto expr2 = std::unique_ptr>( make_expr(&test_fixture::some_const_string, &op::greater_than, std::string("z")) ); assert(!expr2->evaluate(fixture)); - auto expr3 = std::unique_ptr>( + auto expr3 = std::unique_ptr>( make_expr(&test_fixture::some_const_string, &op::greater_than, std::string("this IS 4 T3s7 $tRing ")) ); assert(!expr3->evaluate(fixture)); - auto expr4 = std::unique_ptr>( + auto expr4 = std::unique_ptr>( make_expr(&test_fixture::some_const_string, &op::greater_than, std::string("1234567890")) ); assert(expr4->evaluate(fixture)); - auto expr5 = std::unique_ptr>( + auto expr5 = std::unique_ptr>( make_expr(&test_fixture::some_const_string, &op::greater_than, std::string("A")) ); assert(expr5->evaluate(fixture)); - auto expr6 = std::unique_ptr>( + auto expr6 = std::unique_ptr>( make_expr(&test_fixture::some_const_string, &op::greater_than, std::string("this ")) ); assert(expr6->evaluate(fixture)); - auto expr7 = std::unique_ptr>( + auto expr7 = std::unique_ptr>( make_expr(&test_fixture::some_const_string, &op::greater_than, std::string("stuff")) ); assert(expr7->evaluate(fixture)); - auto expr8 = std::unique_ptr>( + auto expr8 = std::unique_ptr>( make_expr(&test_fixture::some_const_string, &op::greater_than, std::string("abcdefghijklmnopqrstuvwxyz")) ); assert(expr8->evaluate(fixture)); - auto expr9 = std::unique_ptr>( + auto expr9 = std::unique_ptr>( make_expr(&test_fixture::some_const_string, &op::greater_than, std::string("this IS 4 T3s7 $tRing ")) ); assert(!expr9->evaluate(fixture)); - auto expr10 = std::unique_ptr>( + auto expr10 = std::unique_ptr>( make_expr(&test_fixture::some_const_string, &op::greater_than, fixture.some_const_string) ); assert(!expr10->evaluate(fixture)); @@ -222,7 +222,7 @@ void test_string_evaluation() { { // test equals { - auto expr = std::unique_ptr>( + auto expr = std::unique_ptr>( make_expr(&test_fixture::some_string, &op::equals, std::string("hello world!")) ); @@ -247,7 +247,7 @@ void test_string_evaluation() { // test not_equals { - auto expr = std::unique_ptr>( + auto expr = std::unique_ptr>( make_expr(&test_fixture::some_string, &op::not_equals, std::string("hello world!")) ); @@ -272,7 +272,7 @@ void test_string_evaluation() { // test less_than { - auto expr = std::unique_ptr>( + auto expr = std::unique_ptr>( make_expr(&test_fixture::some_string, &op::less_than, std::string("this IS 4 T3s7 $tRing ")) ); @@ -309,7 +309,7 @@ void test_string_evaluation() { // test greater_than { - auto expr = std::unique_ptr>( + auto expr = std::unique_ptr>( make_expr(&test_fixture::some_string, &op::greater_than, std::string("this IS 4 T3s7 $tRing ")) ); @@ -359,7 +359,7 @@ void test_char_ptr_evaluation() { // test equals { // make an expression : given char* == 'a' - auto expr = std::unique_ptr>( + auto expr = std::unique_ptr>( make_expr(&test_fixture::some_char_ptr, &op::equals, &a) ); @@ -385,7 +385,7 @@ void test_char_ptr_evaluation() { // test not_equals { // make an expression : given char* != 'a' - auto expr = std::unique_ptr>( + auto expr = std::unique_ptr>( make_expr(&test_fixture::some_char_ptr, &op::not_equals, &a) ); @@ -411,7 +411,7 @@ void test_char_ptr_evaluation() { // test less_than { // make an expression : given char* < 'b' - auto expr = std::unique_ptr>( + auto expr = std::unique_ptr>( make_expr(&test_fixture::some_char_ptr, &op::less_than, &b) ); @@ -437,7 +437,7 @@ void test_char_ptr_evaluation() { // test greater_than { // make an expression : given char* > 'b' - auto expr = std::unique_ptr>( + auto expr = std::unique_ptr>( make_expr(&test_fixture::some_char_ptr, &op::greater_than, &b) ); @@ -466,7 +466,7 @@ void test_uint_evaluation() { // test equals { - auto expr = std::unique_ptr>( + auto expr = std::unique_ptr>( make_expr(&test_fixture::some_uint, op::equals, (uint16_t) 9001) ); @@ -491,7 +491,7 @@ void test_uint_evaluation() { // test not_equals { - auto expr = std::unique_ptr>( + auto expr = std::unique_ptr>( make_expr(&test_fixture::some_uint, op::not_equals, (uint16_t) 9001) ); @@ -516,7 +516,7 @@ void test_uint_evaluation() { // test less_than { - auto expr = std::unique_ptr>( + auto expr = std::unique_ptr>( make_expr(&test_fixture::some_uint, op::less_than, (uint16_t) 9001) ); @@ -541,7 +541,7 @@ void test_uint_evaluation() { // test greater_than { - auto expr = std::unique_ptr>( + auto expr = std::unique_ptr>( make_expr(&test_fixture::some_uint, op::greater_than, (uint16_t) 9001) ); @@ -570,8 +570,8 @@ void test_const_func_evaluation() { // test equals { - auto expr1 = std::unique_ptr>(make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::equals, true)); - auto expr2 = std::unique_ptr>(make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::equals, false)); + auto expr1 = std::unique_ptr>(make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::equals, true)); + auto expr2 = std::unique_ptr>(make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::equals, false)); fixture.some_uint = 0; assert(!expr1->evaluate(fixture)); @@ -600,8 +600,8 @@ void test_const_func_evaluation() { // test not_equals { - auto expr1 = std::unique_ptr>(make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::not_equals, true)); - auto expr2 = std::unique_ptr>(make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::not_equals, false)); + auto expr1 = std::unique_ptr>(make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::not_equals, true)); + auto expr2 = std::unique_ptr>(make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::not_equals, false)); fixture.some_uint = 0; assert(expr1->evaluate(fixture)); @@ -630,8 +630,8 @@ void test_const_func_evaluation() { // test less_than { - auto expr1 = std::unique_ptr>(make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::less_than, true)); - auto expr2 = std::unique_ptr>(make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::less_than, false)); + auto expr1 = std::unique_ptr>(make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::less_than, true)); + auto expr2 = std::unique_ptr>(make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::less_than, false)); fixture.some_uint = 0; assert(expr1->evaluate(fixture)); @@ -660,8 +660,8 @@ void test_const_func_evaluation() { // test greater_than { - auto expr1 = std::unique_ptr>(make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::greater_than, true)); - auto expr2 = std::unique_ptr>(make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::greater_than, false)); + auto expr1 = std::unique_ptr>(make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::greater_than, true)); + auto expr2 = std::unique_ptr>(make_expr(&test_fixture::is_some_uint_greater_than_zero, &op::greater_than, false)); fixture.some_uint = 0; assert(!expr1->evaluate(fixture)); diff --git a/tests/test_utils.hpp b/tests/test_utils.hpp index 2ecfdac..2cfe300 100644 --- a/tests/test_utils.hpp +++ b/tests/test_utils.hpp @@ -21,9 +21,9 @@ struct test_fixture { * Helper function for creating inner expression tree nodes. I'm undecided if this should be included in the public API. */ template -std::unique_ptr> make_op_node(LeftChild* left, et::boolean_op op, RightChild* right) { +std::unique_ptr> make_op_node(LeftChild* left, et::boolean_op op, RightChild* right) { auto node = new et::node::expression_tree_op_node(op); node->set_left(left); node->set_right(right); - return std::unique_ptr>(node); + return std::unique_ptr>(node); } \ No newline at end of file From 2fc36384899002c75b64d07b1d80c402b531ccb5 Mon Sep 17 00:00:00 2001 From: Noah Attwood Date: Tue, 9 May 2023 21:35:56 -0300 Subject: [PATCH 14/14] implements AND/OR operations on the expression_tree_op_node class --- include/attwoodn/expression_tree.hpp | 60 ++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/include/attwoodn/expression_tree.hpp b/include/attwoodn/expression_tree.hpp index 31bc9ea..cafcb31 100644 --- a/include/attwoodn/expression_tree.hpp +++ b/include/attwoodn/expression_tree.hpp @@ -145,6 +145,66 @@ namespace attwoodn::expression_tree { return false; } + /** + * Performs an AND operation with an expression_tree_leaf_node to create a heap-allocated pointer + * to a new expression_tree_op_node. The returned expression_tree_op_node becomes the parent of both this node + * and the other node that was AND'ed with this node. This node becomes the left child. The other node becomes + * the right child. + */ + template>::value>* = nullptr> + expression_tree_op_node* AND (OtherLeafNode* other) { + auto* op_node = new expression_tree_op_node(boolean_op::AND); + op_node->set_left(this); + op_node->set_right(other); + return op_node; + } + + /** + * Performs an OR operation with an expression_tree_leaf_node to create a heap-allocated pointer + * to a new expression_tree_op_node. The returned expression_tree_op_node becomes the parent of both this node + * and the other node that was OR'ed with this node. This node becomes the left child. The other node becomes + * the right child. + */ + template>::value>* = nullptr> + expression_tree_op_node* OR (OtherLeafNode* other) { + auto* op_node = new expression_tree_op_node(boolean_op::OR); + op_node->set_left(this); + op_node->set_right(other); + return op_node; + } + + /** + * Performs an AND operation with another expression_tree_op_node to create a heap-allocated pointer + * to a new expression_tree_op_node. The returned expression_tree_op_node becomes the parent of both this node + * and the other node that was AND'ed with this node. This node becomes the left child. The other node becomes + * the right child. + */ + template>::value>* = nullptr> + expression_tree_op_node* AND (OtherOpNode* other) { + auto* op_node = new expression_tree_op_node(boolean_op::AND); + op_node->set_left(this); + op_node->set_right(other); + return op_node; + } + + /** + * Performs an OR operation with another expression_tree_op_node to create a heap-allocated pointer + * to a new expression_tree_op_node. The returned expression_tree_op_node becomes the parent of both this node + * and the other node that was OR'ed with this node. This node becomes the left child. The other node becomes + * the right child. + */ + template>::value>* = nullptr> + expression_tree_op_node* OR (OtherOpNode* other) { + auto* op_node = new expression_tree_op_node(boolean_op::OR); + op_node->set_left(this); + op_node->set_right(other); + return op_node; + } + private: boolean_op bool_op_; LeftChild* left_ { nullptr };