Repository: nifi-minifi-cpp Updated Branches: refs/heads/master c4ea662c7 -> 2da613875
MINIFICPP-414 Added boolean Expression Language functions This closes #275. Signed-off-by: Marc Parisi <[email protected]> Project: http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/repo Commit: http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/commit/2da61387 Tree: http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/tree/2da61387 Diff: http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/diff/2da61387 Branch: refs/heads/master Commit: 2da613875f2264ae9ded35e4aa5617d4e7dc0ebc Parents: c4ea662 Author: Andrew I. Christianson <[email protected]> Authored: Mon Mar 12 11:29:41 2018 -0400 Committer: Marc Parisi <[email protected]> Committed: Sun Mar 18 21:15:17 2018 -0400 ---------------------------------------------------------------------- EXPRESSIONS.md | 308 ++++++++++++++++++- extensions/expression-language/Expression.cpp | 125 +++++++- extensions/expression-language/common/Value.h | 23 ++ .../ExpressionLanguageTests.cpp | 272 ++++++++++++++++ 4 files changed, 711 insertions(+), 17 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/2da61387/EXPRESSIONS.md ---------------------------------------------------------------------- diff --git a/EXPRESSIONS.md b/EXPRESSIONS.md index 01ddb64..ee633a7 100644 --- a/EXPRESSIONS.md +++ b/EXPRESSIONS.md @@ -147,6 +147,22 @@ token, filename. ## Supported Features +### Boolean Logic + +- [`isNull`](#isnull) +- [`notNull`](#notnull) +- [`isEmpty`](#isempty) +- [`equals`](#equals) +- [`equalsIgnoreCase`](#equalsignorecase) +- [`gt`](#gt) +- [`ge`](#ge) +- [`lt`](#lt) +- [`le`](#le) +- [`and`](#and) +- [`or`](#or) +- [`not`](#not) +- [`ifElse`](#ifelse) + ### String Manipulation - [`toUpper`](#toupper) @@ -198,22 +214,6 @@ token, filename. - `jsonPath` -### Boolean Logic - -- `isNull` -- `notNull` -- `isEmpty` -- `equals` -- `equalsIgnoreCase` -- `gt` -- `ge` -- `lt` -- `le` -- `and` -- `or` -- `not` -- `ifElse` - ### Encode/Decode Functions - `escapeJson` @@ -266,6 +266,282 @@ planned due to language/environment (Java vs. C++) differences: - `math` +## Boolean Logic + +One of the most powerful features of the Expression Language is the ability to +compare an attribute value against some other value. This is used often, for +example, to configure how a Processor should route data. The following +functions are used for performing boolean logic, such as comparing two values. +Each of these functions are designed to work on values of type Boolean. + +### isNull + +**Description**: The `isNull` function returns `true` if the subject is `null`, +`false` otherwise. This is typically used to determine if an attribute exists. + +**Subject Type**: Any + +**Arguments**: No arguments + +**Return Type**: Boolean + +**Examples**: `${filename:isNull()}` returns true if the `filename` attribute +does not exist. It returns `false` if the attribute exists. + +### notNull + +**Description**: The `notNull` function returns the opposite value of the +`isNull` function. That is, it will return `true` if the subject exists and +`false` otherwise. + +**Subject Type**: Any + +**Arguments**: No arguments + +**Return Type**: Boolean + +**Examples**: `${filename:notNull()}` returns `true` if the `filename` +attribute exists. It returns `false` if the attribute does not exist. + +### isEmpty + +**Description**: The `isEmpty` function returns `true` if the Subject is +`null`, does not contain any characters or contains only white-space (new line, +carriage return, space, tab), `false` otherwise. + +**Subject Type**: String + +**Arguments**: No arguments + +**Return Type**: Boolean + +**Examples**: `${filename:isEmpty()}` returns `true` if the `filename` +attribute does not exist or contains only white space. `${literal(" +"):isEmpty()}` returns `true` as well as `${literal(""):isEmpty()}`. + +### equals + +**Description**: The `equals` function is very widely used and determines if +its subject is equal to another String value. Note that the equals function +performs a direct comparison of two String values. Take care not to confuse +this function with the `matches` function, which evaluates its subject against +a Regular Expression. + +**Subject Type**: Any + +**Arguments**: + +| Argument | Description | +| - | - | +| value | The value to compare the Subject to. Must be same type as the Subject. | + +**Return Type**: Boolean + +**Examples**: We can check if the `filename` of a FlowFile is `hello.txt` by +using the expression `${filename:equals('hello.txt')}`, or we could check if +the value of the attribute `hello` is equal to the value of the `filename` +attribute: `${hello:equals( ${filename} )}`. + +### equalsIgnoreCase + +**Description**: Similar to the equals function, the `equalsIgnoreCase` +function compares its subject against a String value but returns `true` if the +two values differ only by case (upper case vs. lower case). + +**Subject Type**: String + +**Arguments**: + +| Argument | Description | +| - | - | +| value | The value to compare the Subject to. | + +**Return Type**: Boolean + +**Examples**: `${filename:equalsIgnoreCase('hello.txt')}` will evaluate to +`true` if filename is equal to `hello.txt` or `HELLO.TXT` or `HeLLo.TxT`. + +### gt + +**Description**: The `gt` function is used for numeric comparison and returns +`true` if the subject is Greater Than its argument. If either the subject or +the argument cannot be coerced into a Number, this function returns `false`. + +**Subject Type**: Number + +**Arguments**: + +| Argument | Description | +| - | - | +| value | The number to compare the Subject to. | + +**Return Type**: Boolean + +**Examples**: `${fileSize:gt( 1024 )}` will return `true` if the size of the +FlowFileâs content is more than 1 kilobyte (1024 bytes). Otherwise, it will +return `false`. + +### ge + +**Description**: The `ge` function is used for numeric comparison and returns +`true` if the subject is Greater Than Or Equal To its argument. If either the +subject or the argument cannot be coerced into a Number, this function returns +`false`. + +**Subject Type**: Number + +**Arguments**: + +| Argument | Description | +| - | - | +| value | The number to compare the Subject to. | + +**Return Type**: Boolean + +**Examples**: `${fileSize:ge( 1024 )}` will return `true` if the size of the +FlowFileâs content is at least ( is greater than or equal to) 1 kilobyte (1024 +bytes). Otherwise, it will return `false`. + +### lt + +**Description**: The `lt` function is used for numeric comparison and returns +`true` if the subject is Less Than its argument. If either the subject or the +argument cannot be coerced into a Number, this function returns `false`. + +**Subject Type**: Number + +**Arguments**: + +| Argument | Description | +| - | - | +| value | The number to compare the Subject to. | + +**Return Type**: Boolean + +**Examples**: `${fileSize:lt( 1048576 )}` will return `true` if the size of the +FlowFileâs content is less than 1 megabyte (1048576 bytes). Otherwise, it will +return `false`. + +### le + +**Description**: The `le` function is used for numeric comparison and returns +`true` if the subject is Less Than Or Equal To its argument. If either the +subject or the argument cannot be coerced into a Number, this function returns +`false`. + +**Subject Type**: Number + +**Arguments**: + +| Argument | Description | +| - | - | +| value | The number to compare the Subject to. | + +**Return Type**: Boolean + +**Examples**: `${fileSize:le( 1048576 )}` will return `true` if the size of the +FlowFileâs content is at most (less than or equal to) 1 megabyte (1048576 +bytes). Otherwise, it will return `false`. + +### and + +**Description**: The `and` function takes as a single argument a Boolean value +and returns `true` if both the Subject and the argument are `true`. If either +the subject or the argument is `false` or cannot be coerced into a Boolean, the +function returns `false`. Typically, this is used with an embedded Expression +as the argument. + +**Subject Type**: Boolean + +**Arguments**: + +| Argument | Description | +| - | - | +| condition | The right-hand-side of the `and` Expression | + +**Return Type**: Boolean + +**Examples**: We can check if the filename is both all lower-case and has at +least 5 characters by using the Expression + +``` +${filename:toLower():equals( ${filename} ):and( + ${filename:length():ge(5)} +)} +``` + +### or + +**Description**: The `or` function takes as a single argument a Boolean value +and returns `true` if either the Subject or the argument is `true`. If both the +subject and the argument are `false`, the function returns `false`. If either +the Subject or the argument cannot be coerced into a Boolean value, this +function will return `false`. + +**Subject Type**: Boolean + +**Arguments**: + +| Argument | Description | +| - | - | +| condition | The right-hand-side of the `or` Expression | + +**Return Type**: Boolean + +**Examples**: The following example will return `true` if either the filename +has exactly 5 characters or if the filename is all lower-case. + +``` +${filename:toLower():equals( ${filename} ):or( + ${filename:length():equals(5)} +)} +``` + +### not + +**Description**: The `not` function returns the negation of the Boolean value of the subject. + +**Subject Type**: Boolean + +**Arguments**: No arguments + +**Return Type**: Boolean + +**Examples**: We can invert the value of another function by using the not +function, as `${filename:equals('hello.txt'):not()}`. This will return `true` +if the filename is NOT equal to `hello.txt` and will return `false` if the +filename is `hello.txt`. + +### ifElse + +**Description**: Evaluates the first argument if the Subject evaluates to +`true`, or the second argument if the Subject evaluates to `false`. + +**Subject Type**: Boolean + +| Argument | Description | +| - | - | +| EvaluateIfTrue | The value to return if the Subject is `true` | +| EvaluateIfFalse | The value to return if the Subject is `false` | + +**Return Type**: String + +**Examples**: + +If the `filename` attribute has the value `a brand new filename.txt`, the +`nullFilename` attribute has the value `null`, and the `bool` attribute has the +value `true`, then the following expressions will provide the following +results: + +| Expression | Value | +| - | - | +| `${bool:ifElse('a','b')}` | a | +| `${literal(true):ifElse('a','b')}` | a | +| `${nullFilename:isNull():ifElse('file does not exist', 'located file')}` | file does not exist | +| `${nullFilename:ifElse('found', 'not_found')}` | not_found | +| `${filename:ifElse('found', 'not_found')}` | not_found | +| `${filename:isNull():not():ifElse('found', 'not_found')}` | found | + ## String Manipulation Each of the following functions manipulates a String in some way. http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/2da61387/extensions/expression-language/Expression.cpp ---------------------------------------------------------------------- diff --git a/extensions/expression-language/Expression.cpp b/extensions/expression-language/Expression.cpp index 4824f2d..091051f 100644 --- a/extensions/expression-language/Expression.cpp +++ b/extensions/expression-language/Expression.cpp @@ -19,6 +19,8 @@ #include <iostream> #include <iomanip> #include <random> +#include <algorithm> +#include <string> #include <expression/Expression.h> #include <regex> @@ -43,7 +45,7 @@ Expression make_static(std::string val) { } Expression make_dynamic(const std::function<Value(const Parameters ¶ms)> &val_fn) { - return Expression(Value(), std::move(val_fn)); + return Expression(Value(), val_fn); } Expression make_dynamic_attr(const std::string &attribute_id) { @@ -370,6 +372,101 @@ Value expr_literal(const std::vector<Value> &args) { return args[0]; } +Value expr_isNull(const std::vector<Value> &args) { + return Value(args[0].isNull()); +} + +Value expr_notNull(const std::vector<Value> &args) { + return Value(!args[0].isNull()); +} + +Value expr_isEmpty(const std::vector<Value> &args) { + if (args[0].isNull()) { + return Value(true); + } + + std::string arg_0 = args[0].asString(); + + for (char c : arg_0) { + if (c != ' ' + && c != '\f' + && c != '\n' + && c != '\r' + && c != '\t' + && c != '\v') { + return Value(false); + } + } + + return Value(true); +} + +Value expr_equals(const std::vector<Value> &args) { + return Value(args[0].asString() == args[1].asString()); +} + +Value expr_equalsIgnoreCase(const std::vector<Value> &args) { + auto arg_0 = args[0].asString(); + auto arg_1 = args[1].asString(); + + std::transform(arg_0.begin(), arg_0.end(), arg_0.begin(), ::tolower); + std::transform(arg_1.begin(), arg_1.end(), arg_1.begin(), ::tolower); + + return Value(arg_0 == arg_1); +} + +Value expr_gt(const std::vector<Value> &args) { + if (args[0].isDecimal() && args[1].isDecimal()) { + return Value(args[0].asLongDouble() > args[1].asLongDouble()); + } else { + return Value(args[0].asSignedLong() > args[1].asSignedLong()); + } +} + +Value expr_ge(const std::vector<Value> &args) { + if (args[0].isDecimal() && args[1].isDecimal()) { + return Value(args[0].asLongDouble() >= args[1].asLongDouble()); + } else { + return Value(args[0].asSignedLong() >= args[1].asSignedLong()); + } +} + +Value expr_lt(const std::vector<Value> &args) { + if (args[0].isDecimal() && args[1].isDecimal()) { + return Value(args[0].asLongDouble() < args[1].asLongDouble()); + } else { + return Value(args[0].asSignedLong() < args[1].asSignedLong()); + } +} + +Value expr_le(const std::vector<Value> &args) { + if (args[0].isDecimal() && args[1].isDecimal()) { + return Value(args[0].asLongDouble() <= args[1].asLongDouble()); + } else { + return Value(args[0].asSignedLong() <= args[1].asSignedLong()); + } +} + +Value expr_and(const std::vector<Value> &args) { + return Value(args[0].asBoolean() && args[1].asBoolean()); +} + +Value expr_or(const std::vector<Value> &args) { + return Value(args[0].asBoolean() || args[1].asBoolean()); +} + +Value expr_not(const std::vector<Value> &args) { + return Value(!args[0].asBoolean()); +} + +Value expr_ifElse(const std::vector<Value> &args) { + if (args[0].asBoolean()) { + return args[1]; + } else { + return args[2]; + } +} + Expression make_dynamic_function(const std::string &function_name, const std::vector<Expression> &args) { if (function_name == "hostname") { @@ -434,6 +531,32 @@ Expression make_dynamic_function(const std::string &function_name, return make_dynamic_function_incomplete<expr_random>(function_name, args, 0); } else if (function_name == "literal") { return make_dynamic_function_incomplete<expr_literal>(function_name, args, 1); + } else if (function_name == "isNull") { + return make_dynamic_function_incomplete<expr_isNull>(function_name, args, 0); + } else if (function_name == "notNull") { + return make_dynamic_function_incomplete<expr_notNull>(function_name, args, 0); + } else if (function_name == "isEmpty") { + return make_dynamic_function_incomplete<expr_isEmpty>(function_name, args, 0); + } else if (function_name == "equals") { + return make_dynamic_function_incomplete<expr_equals>(function_name, args, 1); + } else if (function_name == "equalsIgnoreCase") { + return make_dynamic_function_incomplete<expr_equalsIgnoreCase>(function_name, args, 1); + } else if (function_name == "gt") { + return make_dynamic_function_incomplete<expr_gt>(function_name, args, 1); + } else if (function_name == "ge") { + return make_dynamic_function_incomplete<expr_ge>(function_name, args, 1); + } else if (function_name == "lt") { + return make_dynamic_function_incomplete<expr_lt>(function_name, args, 1); + } else if (function_name == "le") { + return make_dynamic_function_incomplete<expr_le>(function_name, args, 1); + } else if (function_name == "and") { + return make_dynamic_function_incomplete<expr_and>(function_name, args, 1); + } else if (function_name == "or") { + return make_dynamic_function_incomplete<expr_or>(function_name, args, 1); + } else if (function_name == "not") { + return make_dynamic_function_incomplete<expr_not>(function_name, args, 0); + } else if (function_name == "ifElse") { + return make_dynamic_function_incomplete<expr_ifElse>(function_name, args, 2); } else { std::string msg("Unknown expression function: "); msg.append(function_name); http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/2da61387/extensions/expression-language/common/Value.h ---------------------------------------------------------------------- diff --git a/extensions/expression-language/common/Value.h b/extensions/expression-language/common/Value.h index 75c80bc..b6ac71a 100644 --- a/extensions/expression-language/common/Value.h +++ b/extensions/expression-language/common/Value.h @@ -19,6 +19,7 @@ #include <sstream> #include <iomanip> #include <limits> +#include <algorithm> #ifndef NIFI_MINIFI_CPP_VALUE_H #define NIFI_MINIFI_CPP_VALUE_H @@ -215,6 +216,28 @@ class Value { } } + bool asBoolean() const { + if (is_bool_) { + return bool_val_; + } + if (is_signed_long_) { + return signed_long_val_ != 0; + } else if (is_unsigned_long_) { + return unsigned_long_val_ != 0; + } else if (is_long_double_) { + return long_double_val_ != 0.0; + } else if (is_string_) { + std::string bool_str = string_val_; + std::transform(bool_str.begin(), bool_str.end(), bool_str.begin(), ::tolower); + std:: istringstream bools(bool_str); + bool bool_val; + bools >> std::boolalpha >> bool_val; + return bool_val; + } else { + return false; + } + } + private: bool is_null_ = true; bool is_string_ = false; http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/2da61387/libminifi/test/expression-language-tests/ExpressionLanguageTests.cpp ---------------------------------------------------------------------- diff --git a/libminifi/test/expression-language-tests/ExpressionLanguageTests.cpp b/libminifi/test/expression-language-tests/ExpressionLanguageTests.cpp index de3292e..a4a8899 100644 --- a/libminifi/test/expression-language-tests/ExpressionLanguageTests.cpp +++ b/libminifi/test/expression-language-tests/ExpressionLanguageTests.cpp @@ -744,3 +744,275 @@ TEST_CASE("Chained call 3", "[expressionChainedCall3]") { // NOLINT flow_file_a->addAttribute("attr", "7"); REQUIRE("238" == expr({flow_file_a}).asString()); } + +TEST_CASE("Is Null", "[expressionIsNull]") { // NOLINT + auto expr = expression::compile("${filename:isNull()}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("attr", "7"); + REQUIRE("true" == expr({flow_file_a}).asString()); +} + +TEST_CASE("Is Null 2", "[expressionIsNull2]") { // NOLINT + auto expr = expression::compile("${filename:isNull()}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("filename", "7"); + REQUIRE("false" == expr({flow_file_a}).asString()); +} + +TEST_CASE("Not Null", "[expressionNotNull]") { // NOLINT + auto expr = expression::compile("${filename:notNull()}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("attr", "7"); + REQUIRE("false" == expr({flow_file_a}).asString()); +} + +TEST_CASE("Not Null 2", "[expressionNotNull2]") { // NOLINT + auto expr = expression::compile("${filename:notNull()}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("filename", "7"); + REQUIRE("true" == expr({flow_file_a}).asString()); +} + +TEST_CASE("Is Empty", "[expressionIsEmpty]") { // NOLINT + auto expr = expression::compile("${filename:isEmpty()}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("attr", "7"); + REQUIRE("true" == expr({flow_file_a}).asString()); +} + +TEST_CASE("Is Empty 2", "[expressionIsEmpty2]") { // NOLINT + auto expr = expression::compile("${attr:isEmpty()}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("attr", "7"); + REQUIRE("false" == expr({flow_file_a}).asString()); +} + +TEST_CASE("Is Empty 3", "[expressionIsEmpty3]") { // NOLINT + auto expr = expression::compile("${attr:isEmpty()}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("attr", " \t\r\n "); + REQUIRE("true" == expr({flow_file_a}).asString()); +} + +TEST_CASE("Is Empty 4", "[expressionIsEmpty4]") { // NOLINT + auto expr = expression::compile("${attr:isEmpty()}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("attr", ""); + REQUIRE("true" == expr({flow_file_a}).asString()); +} + +TEST_CASE("Is Empty 5", "[expressionIsEmpty5]") { // NOLINT + auto expr = expression::compile("${attr:isEmpty()}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("attr", " \t\r\n a \t\r\n "); + REQUIRE("false" == expr({flow_file_a}).asString()); +} + +TEST_CASE("Equals", "[expressionEquals]") { // NOLINT + auto expr = expression::compile("${attr:equals('hello.txt')}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("attr", "hello.txt"); + REQUIRE("true" == expr({flow_file_a}).asString()); +} + +TEST_CASE("Equals 2", "[expressionEquals2]") { // NOLINT + auto expr = expression::compile("${attr:equals('hello.txt')}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("attr", "helllo.txt"); + REQUIRE("false" == expr({flow_file_a}).asString()); +} + +TEST_CASE("Equals 3", "[expressionEquals3]") { // NOLINT + auto expr = expression::compile("${attr:plus(5):equals(6)}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("attr", "1"); + REQUIRE("true" == expr({flow_file_a}).asString()); +} + +TEST_CASE("Equals Ignore Case", "[expressionEqualsIgnoreCase]") { // NOLINT + auto expr = expression::compile("${attr:equalsIgnoreCase('hElLo.txt')}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("attr", "hello.txt"); + REQUIRE("true" == expr({flow_file_a}).asString()); +} + +TEST_CASE("Equals Ignore Case 2", "[expressionEqualsIgnoreCase2]") { // NOLINT + auto expr = expression::compile("${attr:plus(5):equalsIgnoreCase(6)}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("attr", "1"); + REQUIRE("true" == expr({flow_file_a}).asString()); +} + +TEST_CASE("GT", "[expressionGT]") { // NOLINT + auto expr = expression::compile("${attr:plus(5):gt(5)}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("attr", "1"); + REQUIRE("true" == expr({flow_file_a}).asString()); +} + +TEST_CASE("GT2", "[expressionGT2]") { // NOLINT + auto expr = expression::compile("${attr:plus(5.1):gt(6.05)}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("attr", "1"); + REQUIRE("true" == expr({flow_file_a}).asString()); +} + +TEST_CASE("GT3", "[expressionGT3]") { // NOLINT + auto expr = expression::compile("${attr:plus(5.1):gt(6.15)}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("attr", "1"); + REQUIRE("false" == expr({flow_file_a}).asString()); +} + +TEST_CASE("GE", "[expressionGE]") { // NOLINT + auto expr = expression::compile("${attr:plus(5):ge(6)}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("attr", "1"); + REQUIRE("true" == expr({flow_file_a}).asString()); +} + +TEST_CASE("GE2", "[expressionGE2]") { // NOLINT + auto expr = expression::compile("${attr:plus(5.1):ge(6.05)}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("attr", "1"); + REQUIRE("true" == expr({flow_file_a}).asString()); +} + +TEST_CASE("GE3", "[expressionGE3]") { // NOLINT + auto expr = expression::compile("${attr:plus(5.1):ge(6.15)}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("attr", "1"); + REQUIRE("false" == expr({flow_file_a}).asString()); +} + +TEST_CASE("LT", "[expressionLT]") { // NOLINT + auto expr = expression::compile("${attr:plus(5):lt(5)}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("attr", "1"); + REQUIRE("false" == expr({flow_file_a}).asString()); +} + +TEST_CASE("LT2", "[expressionLT2]") { // NOLINT + auto expr = expression::compile("${attr:plus(5.1):lt(6.05)}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("attr", "1"); + REQUIRE("false" == expr({flow_file_a}).asString()); +} + +TEST_CASE("LT3", "[expressionLT3]") { // NOLINT + auto expr = expression::compile("${attr:plus(5.1):lt(6.15)}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("attr", "1"); + REQUIRE("true" == expr({flow_file_a}).asString()); +} + +TEST_CASE("LE", "[expressionLE]") { // NOLINT + auto expr = expression::compile("${attr:plus(5):le(6)}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("attr", "1"); + REQUIRE("true" == expr({flow_file_a}).asString()); +} + +TEST_CASE("LE2", "[expressionLE2]") { // NOLINT + auto expr = expression::compile("${attr:plus(5.1):le(6.05)}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("attr", "1"); + REQUIRE("false" == expr({flow_file_a}).asString()); +} + +TEST_CASE("LE3", "[expressionLE3]") { // NOLINT + auto expr = expression::compile("${attr:plus(5.1):le(6.15)}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("attr", "1"); + REQUIRE("true" == expr({flow_file_a}).asString()); +} + +TEST_CASE("And", "[expressionAnd]") { // NOLINT + auto expr = expression::compile("${filename:toLower():equals( ${filename} ):and(${filename:substring(0, 2):equals('an')})}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("filename", "an example file.txt"); + REQUIRE("true" == expr({flow_file_a}).asString()); +} + +TEST_CASE("And 2", "[expressionAnd2]") { // NOLINT + auto expr = expression::compile("${filename:toLower():equals( ${filename} ):and(${filename:substring(0, 2):equals('ab')})}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("filename", "an example file.txt"); + REQUIRE("false" == expr({flow_file_a}).asString()); +} + +TEST_CASE("Or", "[expressionOr]") { // NOLINT + auto expr = expression::compile("${filename:toLower():equals( ${filename} ):or(${filename:substring(0, 2):equals('an')})}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("filename", "an example file.txt"); + REQUIRE("true" == expr({flow_file_a}).asString()); +} + +TEST_CASE("Or 2", "[expressionOr2]") { // NOLINT + auto expr = expression::compile("${filename:toLower():equals( ${filename} ):or(${filename:substring(0, 2):equals('ab')})}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("filename", "an example file.txt"); + REQUIRE("true" == expr({flow_file_a}).asString()); +} + +TEST_CASE("Not", "[expressionNot]") { // NOLINT + auto expr = expression::compile("${filename:toLower():equals( ${filename} ):and(${filename:substring(0, 2):equals('an')}):not()}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("filename", "an example file.txt"); + REQUIRE("false" == expr({flow_file_a}).asString()); +} + +TEST_CASE("Not 2", "[expressionNot2]") { // NOLINT + auto expr = expression::compile("${filename:toLower():equals( ${filename} ):and(${filename:substring(0, 2):equals('ab')}):not()}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("filename", "an example file.txt"); + REQUIRE("true" == expr({flow_file_a}).asString()); +} + +TEST_CASE("If Else", "[expressionIfElse]") { // NOLINT + auto expr = expression::compile("${filename:toLower():equals( ${filename}):ifElse('yes', 'no')}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("filename", "an example file.txt"); + REQUIRE("yes" == expr({flow_file_a}).asString()); +} + +TEST_CASE("If Else 2", "[expressionIfElse2]") { // NOLINT + auto expr = expression::compile("${filename:toLower():equals( ${filename}):ifElse('yes', 'no')}"); + + auto flow_file_a = std::make_shared<MockFlowFile>(); + flow_file_a->addAttribute("filename", "An example file.txt"); + REQUIRE("no" == expr({flow_file_a}).asString()); +}
