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 &params)> 
&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());
+}

Reply via email to