This is an automated email from the ASF dual-hosted git repository.

adebreceni pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi-minifi-cpp.git


The following commit(s) were added to refs/heads/main by this push:
     new d640a1e  MINIFICPP-1703 ExecuteScript Lua documentation, tests and 
fixes
d640a1e is described below

commit d640a1ee6de8e6e229e06edba56e31181fd5aab8
Author: Martin Zink <[email protected]>
AuthorDate: Mon Jan 3 13:42:06 2022 +0100

    MINIFICPP-1703 ExecuteScript Lua documentation, tests and fixes
    
    Signed-off-by: Adam Debreceni <[email protected]>
    
    This closes #1228
---
 CMakeLists.txt                                     |  2 +-
 PROCESSORS.md                                      | 20 ++---
 examples/README.md                                 | 17 ++++
 examples/process_data_with_scripts.yml             | 99 ++++++++++++++++++++++
 examples/scripts/README.md                         | 29 +++++++
 examples/scripts/lua/heads_or_tails.lua            | 30 +++++++
 examples/scripts/lua/reverse_flow_file_content.lua | 42 +++++++++
 .../scripts/python/heads_or_tails.py               | 13 ++-
 .../scripts/python/reverse_flow_file_content.py    | 35 ++++----
 extensions/script/ExecuteScript.cpp                | 92 +++++++++-----------
 extensions/script/lua/LuaScriptEngine.cpp          | 12 ++-
 extensions/script/lua/LuaScriptEngine.h            | 14 ++-
 .../script/python/ExecutePythonProcessor.cpp       | 24 ++----
 extensions/script/python/PythonScriptEngine.h      |  2 +-
 .../script/tests}/CMakeLists.txt                   | 16 ++--
 .../script/tests}/ExecutePythonProcessorTests.cpp  |  2 +-
 extensions/script/tests/LuaScriptEngineTests.cpp   | 46 ++++++++++
 .../script/tests/PythonScriptEngineTests.cpp       | 44 ++++++++++
 .../TestExecuteScriptProcessorWithLuaScript.cpp    | 53 ++++++------
 .../TestExecuteScriptProcessorWithPythonScript.cpp |  2 +-
 .../test_scripts/non_transferring_processor.py     |  0
 ...passthrough_processor_transfering_to_failure.py |  0
 ...passthrough_processor_transfering_to_success.py |  0
 .../tests}/test_scripts/stateful_processor.py      |  0
 libminifi/test/Utils.h                             | 21 +++++
 25 files changed, 474 insertions(+), 141 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 25caf38..d687c1a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -500,7 +500,7 @@ endif()
 ## Scripting extensions
 if (ENABLE_ALL OR ENABLE_SCRIPTING)
        include(Sol2)
-       createExtension(SCRIPTING-EXTENSIONS "SCRIPTING EXTENSIONS" "This 
enables scripting" "extensions/script" "${TEST_DIR}/script-tests")
+       createExtension(SCRIPTING-EXTENSIONS "SCRIPTING EXTENSIONS" "This 
enables scripting" "extensions/script" "extensions/script/tests")
 endif()
 
 # Sensors extensions
diff --git a/PROCESSORS.md b/PROCESSORS.md
index 47fdbf0..fb796af 100644
--- a/PROCESSORS.md
+++ b/PROCESSORS.md
@@ -463,19 +463,19 @@ Executes a script given the flow file and a process 
session. The script is respo
 
 In the list below, the names of required properties appear in bold. Any other 
properties (not in bold) are considered optional. The table also indicates any 
default values, and whether a property supports the NiFi Expression Language.
 
-| Name | Default Value | Allowable Values | Description |
-| - | - | - | - |
-|Module Directory|||Comma-separated list of paths to files and/or directories 
which contain modules required by the script|
-|Script Body|||Body of script to execute. Only one of Script File or Script 
Body may be used|
-|Script Engine|python||The engine to execute scripts (python, lua)|
-|Script File|||Path to script file to execute. Only one of Script File or 
Script Body may be used|
+| Name                 | Default Value | Allowable Values | Description        
                                                                                
    |
+|----------------------|---------------|------------------|--------------------------------------------------------------------------------------------------------|
+| Module Directory     |               |                  | Comma-separated 
list of paths to files and/or directories which contain modules required by the 
script |
+| Script Body          |               |                  | Body of script to 
execute. Only one of Script File or Script Body may be used                     
     |
+| Script Engine        | python        | python<br>lua    | The engine to 
execute scripts (python, lua)                                                   
         |
+| Script File          |               |                  | Path to script 
file to execute. Only one of Script File or Script Body may be used             
        |
 
 ### Relationships
 
-| Name | Description |
-| - | - |
-|failure|Script failures|
-|success|Script successes|
+| Name    | Description      |
+|---------|------------------|
+| failure | Script failures  |
+| success | Script successes |
 
 
 ## ExtractText
diff --git a/examples/README.md b/examples/README.md
index 5c154d7..788bf47 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -39,6 +39,9 @@ The following examples show simple flow configurations for 
several common use ca
   - [List and Fetch Content from AWS S3 
Bucket](#list-and-fetch-content-from-aws-s3-bucket)
 - [SQL Operations](#sql-operations)
   - [Query Database Table](#query-database-table)
+- [ExecuteScript](#ExecuteScript)
+  - [Reverse flowfile's content with lua](#reverse-content-with-lua)
+  - [Reverse flowfile's content with python](#reverse-content-with-python)
 
 ## Filesystem Operations
 
@@ -139,3 +142,17 @@ The flow: ListS3 &#10132; FetchS3Object &#10132; 
LogAttribute
 Using the [querydbtable_config.yml](querydbtable_config.yml) flow 
configuration MiNiFi queries the `id` and `name` columns of the `users` table 
with a `where` clause and the results are put in the `/tmp/output` directory. 
The database connection data is set in the `ODBCService` controller service.
 
 The flow: QueryDatabaseTable &#10132; PutFile
+
+## ExecuteScript
+
+ExecuteScript supports [Lua](https://www.lua.org/) and 
[Python](https://www.python.org/)
+
+### Reverse Content with Scripts
+
+Using the [process_data_with_scripts.yml](process_data_with_scripts.yml) flow 
configuration MiNiFi generates a flowfile
+then reverses its content with 
[reverse_flow_file_content.py](scripts/python/reverse_flow_file_content.py) or 
[reverse_flow_file_content.lua](scripts/lua/reverse_flow_file_content.lua) 
+then writes the result to _./reversed_flow_files/_
+
+The flow: [GenerateFlowFile](../PROCESSORS.md#generateflowfile) &#10132; 
[ExecuteScript](../PROCESSORS.md#executescript) &#10132;  
[PutFile](../PROCESSORS.md#putfile) 
+
+Additional script examples can be found [here](scripts/README.md).
diff --git a/examples/process_data_with_scripts.yml 
b/examples/process_data_with_scripts.yml
new file mode 100644
index 0000000..552f579
--- /dev/null
+++ b/examples/process_data_with_scripts.yml
@@ -0,0 +1,99 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+MiNiFi Config Version: 3
+Flow Controller:
+  name: MiNiFi Flow
+Processors:
+  - id: 9e5dc411-cc01-4359-adea-ff70f07b3402
+    name: ExecuteScript
+    class: org.apache.nifi.minifi.processors.ExecuteScript
+    max concurrent tasks: 1
+    scheduling strategy: TIMER_DRIVEN
+    scheduling period: 1000 ms
+    penalization period: 30000 ms
+    yield period: 1000 ms
+    run duration nanos: 0
+    auto-terminated relationships list:
+      - failure
+    Properties:
+      Module Directory:
+      Script Body:
+      Script Engine: python
+#     Script Engine: lua
+      Script File: ../../examples/scripts/python/reverse_flow_file_content.py
+#     Script File: ../../examples/scripts/lua/reverse_flow_file_content.lua
+  - id: b3972d8e-f0de-420a-a259-4d46017699e7
+    name: GenerateFlowFile
+    class: org.apache.nifi.minifi.processors.GenerateFlowFile
+    max concurrent tasks: 1
+    scheduling strategy: TIMER_DRIVEN
+    scheduling period: 1000 ms
+    penalization period: 30000 ms
+    yield period: 1000 ms
+    run duration nanos: 0
+    auto-terminated relationships list: []
+    Properties:
+      Batch Size: '1'
+      Custom Text: 'here is a generated uuid: ${UUID()}'
+      Data Format: Text
+      File Size: 1 kB
+      Unique FlowFiles: 'false'
+  - id: ea186cb6-48e6-40e6-98f6-b78568d9f91d
+    name: PutFile
+    class: org.apache.nifi.minifi.processors.PutFile
+    max concurrent tasks: 1
+    scheduling strategy: TIMER_DRIVEN
+    scheduling period: 1000 ms
+    penalization period: 30000 ms
+    yield period: 1000 ms
+    run duration nanos: 0
+    auto-terminated relationships list:
+      - failure
+      - success
+    Properties:
+      Conflict Resolution Strategy: fail
+      Create Missing Directories: 'true'
+      Directory: ./reversed_flow_files/
+      Directory Permissions:
+      Maximum File Count: '-1'
+      Permissions:
+Controller Services: []
+Process Groups: []
+Input Ports: []
+Output Ports: []
+Funnels: []
+Connections:
+  - id: 938cc13f-ae5a-43fc-ab10-e71cecef652e
+    name: ExecuteScript/success/PutFile
+    source id: 9e5dc411-cc01-4359-adea-ff70f07b3402
+    source relationship names:
+      - success
+    destination id: ea186cb6-48e6-40e6-98f6-b78568d9f91d
+    max work queue size: 10000
+    max work queue data size: 10 MB
+    flowfile expiration: 0 seconds
+    queue prioritizer class: ''
+  - id: 7612fcc2-c80e-4967-aeec-2b1076376f49
+    name: GenerateFlowFile/success/ExecuteScript
+    source id: b3972d8e-f0de-420a-a259-4d46017699e7
+    source relationship names:
+      - success
+    destination id: 9e5dc411-cc01-4359-adea-ff70f07b3402
+    max work queue size: 10000
+    max work queue data size: 10 MB
+    flowfile expiration: 0 seconds
+    queue prioritizer class: ''
+Remote Process Groups: []
diff --git a/examples/scripts/README.md b/examples/scripts/README.md
new file mode 100644
index 0000000..8be6071
--- /dev/null
+++ b/examples/scripts/README.md
@@ -0,0 +1,29 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+      http://www.apache.org/licenses/LICENSE-2.0
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+# ExecuteScript Examples
+
+The following examples show how scripts can be integrated into MiNiFi using 
the [ExecuteScript](../../PROCESSORS.md#executescript) processor.
+
+## Heads or Tails
+This script will generate empty flowfiles and transfer them randomly to the 
Success or Failure relationship of the ExecuteScript.
+- [heads_or_tails.lua](lua/heads_or_tails.lua)
+- [heads_or_tails.py](python/heads_or_tails.py)
+
+## Reverse FlowFile's content
+This script reverses the content of the incoming flowfiles, and adds the 
current timestamp as an attribute.  
+- [reverse_flow_file_content.lua](lua/reverse_flow_file_content.lua)
+- [reverse_flow_file_content.py](python/reverse_flow_file_content.py)
+
+
diff --git a/examples/scripts/lua/heads_or_tails.lua 
b/examples/scripts/lua/heads_or_tails.lua
new file mode 100644
index 0000000..3e94be4
--- /dev/null
+++ b/examples/scripts/lua/heads_or_tails.lua
@@ -0,0 +1,30 @@
+---
+---  Licensed to the Apache Software Foundation (ASF) under one or more
+---  contributor license agreements.  See the NOTICE file distributed with
+---  this work for additional information regarding copyright ownership.
+---  The ASF licenses this file to You under the Apache License, Version 2.0
+---  (the "License"); you may not use this file except in compliance with
+---  the License.  You may obtain a copy of the License at
+---
+---      http://www.apache.org/licenses/LICENSE-2.0
+---
+---  Unless required by applicable law or agreed to in writing, software
+---  distributed under the License is distributed on an "AS IS" BASIS,
+---  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+---  See the License for the specific language governing permissions and
+---  limitations under the License.
+---
+
+math.randomseed(os.time())
+
+function onTrigger(context, session)
+    flow_file = session:create()
+
+    if flow_file ~= nil then
+        if math.random() > 0.5 then
+            session:transfer(flow_file, REL_SUCCESS)
+        else
+            session:transfer(flow_file, REL_FAILURE)
+        end
+    end
+end
diff --git a/examples/scripts/lua/reverse_flow_file_content.lua 
b/examples/scripts/lua/reverse_flow_file_content.lua
new file mode 100644
index 0000000..66ff2d4
--- /dev/null
+++ b/examples/scripts/lua/reverse_flow_file_content.lua
@@ -0,0 +1,42 @@
+---
+---  Licensed to the Apache Software Foundation (ASF) under one or more
+---  contributor license agreements.  See the NOTICE file distributed with
+---  this work for additional information regarding copyright ownership.
+---  The ASF licenses this file to You under the Apache License, Version 2.0
+---  (the "License"); you may not use this file except in compliance with
+---  the License.  You may obtain a copy of the License at
+---
+---      http://www.apache.org/licenses/LICENSE-2.0
+---
+---  Unless required by applicable law or agreed to in writing, software
+---  distributed under the License is distributed on an "AS IS" BASIS,
+---  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+---  See the License for the specific language governing permissions and
+---  limitations under the License.
+---
+
+read_callback = {}
+write_reverse_string_callback = {}
+
+function read_callback.process(self, input_stream)
+    self['content'] = input_stream:read(0)
+    return #self['content']
+end
+
+function write_reverse_string_callback.process(self, output_stream)
+    reversed_content = string.reverse(self['content'])
+    output_stream:write(reversed_content)
+    return #reversed_content
+end
+
+function onTrigger(context, session)
+    flow_file = session:get()
+
+    if flow_file ~= nil then
+        session:read(flow_file, read_callback)
+        write_reverse_string_callback['content'] = read_callback['content']
+        session:write(flow_file, write_reverse_string_callback)
+        flow_file:addAttribute('lua_timestamp', tostring(os.time()))
+        session:transfer(flow_file, REL_SUCCESS)
+    end
+end
diff --git 
a/libminifi/test/script-tests/test_scripts/non_transferring_processor.py 
b/examples/scripts/python/heads_or_tails.py
similarity index 74%
copy from libminifi/test/script-tests/test_scripts/non_transferring_processor.py
copy to examples/scripts/python/heads_or_tails.py
index afb83d4..f54ca68 100644
--- a/libminifi/test/script-tests/test_scripts/non_transferring_processor.py
+++ b/examples/scripts/python/heads_or_tails.py
@@ -1,5 +1,4 @@
 #
-#
 #  Licensed to the Apache Software Foundation (ASF) under one or more
 #  contributor license agreements.  See the NOTICE file distributed with
 #  this work for additional information regarding copyright ownership.
@@ -16,14 +15,14 @@
 #  limitations under the License.
 #
 
-
-def describe(processor):
-    processor.setDescription("Processor used for testing in 
ExecutePythonProcessorTests.cpp")
+from random import random
 
 
 def onTrigger(context, session):
-    flow_file = session.get()
-    log.info('Vrrm, vrrrm, processor is running, vrrrm!!')
+    flow_file = session.create()
 
     if flow_file is not None:
-        log.info('created flow file: %s' % flow_file.getAttribute('filename'))
+        if random() > 0.5:
+            session.transfer(flow_file, REL_SUCCESS)
+        else:
+            session.transfer(flow_file, REL_FAILURE)
diff --git a/libminifi/test/script-tests/test_scripts/stateful_processor.py 
b/examples/scripts/python/reverse_flow_file_content.py
similarity index 57%
copy from libminifi/test/script-tests/test_scripts/stateful_processor.py
copy to examples/scripts/python/reverse_flow_file_content.py
index 2cfa072..66e76c0 100644
--- a/libminifi/test/script-tests/test_scripts/stateful_processor.py
+++ b/examples/scripts/python/reverse_flow_file_content.py
@@ -1,5 +1,4 @@
 #
-#
 #  Licensed to the Apache Software Foundation (ASF) under one or more
 #  contributor license agreements.  See the NOTICE file distributed with
 #  this work for additional information regarding copyright ownership.
@@ -16,31 +15,31 @@
 #  limitations under the License.
 #
 
+import codecs
+import time
 
-def describe(processor):
-    processor.setDescription("Processor used for testing in 
ExecutePythonProcessorTests.cpp")
 
+class ReadCallback:
+    def process(self, input_stream):
+        self.content = codecs.getreader('utf-8')(input_stream).read()
+        return len(self.content)
 
-state = 0
 
+class WriteReverseStringCallback:
+    def __init__(self, content):
+        self.content = content
 
-class WriteCallback(object):
     def process(self, output_stream):
-        global state
-        new_content = str(state).encode('utf-8')
-        output_stream.write(new_content)
-        state = state + 1
-        return len(new_content)
+        reversed_content = self.content[::-1]
+        output_stream.write(reversed_content.encode('utf-8'))
+        return len(reversed_content)
 
 
 def onTrigger(context, session):
-    global state
-    log.info('Vrrm, vrrrm, processor is running, vrrrm!!')
-    # flow_file = session.get()
-    flow_file = session.create()
-    flow_file.setAttribute("filename", str(state))
-    log.info('created flow file: %s' % flow_file.getAttribute('filename'))
-
+    flow_file = session.get()
     if flow_file is not None:
-        session.write(flow_file, WriteCallback())
+        read_callback = ReadCallback()
+        session.read(flow_file, read_callback)
+        session.write(flow_file, 
WriteReverseStringCallback(read_callback.content))
+        flow_file.addAttribute('python_timestamp', str(int(time.time())))
         session.transfer(flow_file, REL_SUCCESS)
diff --git a/extensions/script/ExecuteScript.cpp 
b/extensions/script/ExecuteScript.cpp
index 4af4a1e..ee69175 100644
--- a/extensions/script/ExecuteScript.cpp
+++ b/extensions/script/ExecuteScript.cpp
@@ -79,7 +79,7 @@ void ExecuteScript::onSchedule(core::ProcessContext *context, 
core::ProcessSessi
   context->getProperty(ScriptBody.getName(), script_body_);
   context->getProperty(ModuleDirectory.getName(), module_directory_);
 
-  if (script_file_.empty() && script_engine_.empty()) {
+  if (script_file_.empty() && script_body_.empty()) {
     logger_->log_error("Either Script Body or Script File must be defined");
     return;
   }
@@ -87,72 +87,64 @@ void ExecuteScript::onSchedule(core::ProcessContext 
*context, core::ProcessSessi
 
 void ExecuteScript::onTrigger(const std::shared_ptr<core::ProcessContext> 
&context,
                               const std::shared_ptr<core::ProcessSession> 
&session) {
-  try {
-    std::shared_ptr<script::ScriptEngine> engine;
+  std::shared_ptr<script::ScriptEngine> engine;
 
-    // Use an existing engine, if one is available
-    if (script_engine_q_.try_dequeue(engine)) {
-      logger_->log_debug("Using available %s script engine instance", 
script_engine_);
-    } else {
-      logger_->log_info("Creating new %s script instance", script_engine_);
-      logger_->log_info("Approximately %d %s script instances created for this 
processor",
-                        script_engine_q_.size_approx(),
-                        script_engine_);
+  // Use an existing engine, if one is available
+  if (script_engine_q_.try_dequeue(engine)) {
+    logger_->log_debug("Using available %s script engine instance", 
script_engine_);
+  } else {
+    logger_->log_info("Creating new %s script instance", script_engine_);
+    logger_->log_info("Approximately %d %s script instances created for this 
processor",
+                      script_engine_q_.size_approx(),
+                      script_engine_);
 
-      if (script_engine_ == "python") {
+    if (script_engine_ == "python") {
 #ifdef PYTHON_SUPPORT
-        engine = createEngine<python::PythonScriptEngine>();
+      engine = createEngine<python::PythonScriptEngine>();
 #else
-        throw std::runtime_error("Python support is disabled in this build.");
+      throw std::runtime_error("Python support is disabled in this build.");
 #endif  // PYTHON_SUPPORT
-      } else if (script_engine_ == "lua") {
+    } else if (script_engine_ == "lua") {
 #ifdef LUA_SUPPORT
-        engine = createEngine<lua::LuaScriptEngine>();
+      engine = createEngine<lua::LuaScriptEngine>();
 #else
-        throw std::runtime_error("Lua support is disabled in this build.");
+      throw std::runtime_error("Lua support is disabled in this build.");
 #endif  // LUA_SUPPORT
-      }
-
-      if (engine == nullptr) {
-        throw std::runtime_error("No script engine available");
-      }
-
-      if (!script_body_.empty()) {
-        engine->eval(script_body_);
-      } else if (!script_file_.empty()) {
-        engine->evalFile(script_file_);
-      } else {
-        throw std::runtime_error("Neither Script Body nor Script File is 
available to execute");
-      }
     }
 
-    if (script_engine_ == "python") {
+    if (engine == nullptr) {
+      throw std::runtime_error("No script engine available");
+    }
+
+    if (!script_body_.empty()) {
+      engine->eval(script_body_);
+    } else if (!script_file_.empty()) {
+      engine->evalFile(script_file_);
+    } else {
+      throw std::runtime_error("Neither Script Body nor Script File is 
available to execute");
+    }
+  }
+
+  if (script_engine_ == "python") {
 #ifdef PYTHON_SUPPORT
-      triggerEngineProcessor<python::PythonScriptEngine>(engine, context, 
session);
+    triggerEngineProcessor<python::PythonScriptEngine>(engine, context, 
session);
 #else
-      throw std::runtime_error("Python support is disabled in this build.");
+    throw std::runtime_error("Python support is disabled in this build.");
 #endif  // PYTHON_SUPPORT
-    } else if (script_engine_ == "lua") {
+  } else if (script_engine_ == "lua") {
 #ifdef LUA_SUPPORT
-      triggerEngineProcessor<lua::LuaScriptEngine>(engine, context, session);
+    triggerEngineProcessor<lua::LuaScriptEngine>(engine, context, session);
 #else
-      throw std::runtime_error("Lua support is disabled in this build.");
+    throw std::runtime_error("Lua support is disabled in this build.");
 #endif  // LUA_SUPPORT
-    }
+  }
 
-    // Make engine available for use again
-    if (script_engine_q_.size_approx() < getMaxConcurrentTasks()) {
-      logger_->log_debug("Releasing %s script engine", script_engine_);
-      script_engine_q_.enqueue(engine);
-    } else {
-      logger_->log_info("Destroying script engine because it is no longer 
needed");
-    }
-  } catch (std::exception &exception) {
-    logger_->log_error("Caught Exception %s", exception.what());
-    this->yield();
-  } catch (...) {
-    logger_->log_error("Caught Exception");
-    this->yield();
+  // Make engine available for use again
+  if (script_engine_q_.size_approx() < getMaxConcurrentTasks()) {
+    logger_->log_debug("Releasing %s script engine", script_engine_);
+    script_engine_q_.enqueue(engine);
+  } else {
+    logger_->log_info("Destroying script engine because it is no longer 
needed");
   }
 }
 
diff --git a/extensions/script/lua/LuaScriptEngine.cpp 
b/extensions/script/lua/LuaScriptEngine.cpp
index f7538d6..4a8dc25 100644
--- a/extensions/script/lua/LuaScriptEngine.cpp
+++ b/extensions/script/lua/LuaScriptEngine.cpp
@@ -64,11 +64,19 @@ LuaScriptEngine::LuaScriptEngine()
 }
 
 void LuaScriptEngine::eval(const std::string &script) {
-  lua_.script(script);
+  try {
+    lua_.script(script, sol::script_throw_on_error);
+  } catch (std::exception& e) {
+    throw minifi::script::ScriptException(e.what());
+  }
 }
 
 void LuaScriptEngine::evalFile(const std::string &file_name) {
-  lua_.script_file(file_name);
+  try {
+    lua_.script_file(file_name, sol::script_throw_on_error);
+  } catch (std::exception& e) {
+    throw minifi::script::ScriptException(e.what());
+  }
 }
 
 } /* namespace lua */
diff --git a/extensions/script/lua/LuaScriptEngine.h 
b/extensions/script/lua/LuaScriptEngine.h
index e6868f6..0e78f71 100644
--- a/extensions/script/lua/LuaScriptEngine.h
+++ b/extensions/script/lua/LuaScriptEngine.h
@@ -27,6 +27,7 @@
 
 #include "../ScriptEngine.h"
 #include "../ScriptProcessContext.h"
+#include "../ScriptException.h"
 
 #include "LuaProcessSession.h"
 
@@ -50,8 +51,17 @@ class LuaScriptEngine : public script::ScriptEngine {
    */
   template<typename... Args>
   void call(const std::string &fn_name, Args &&...args) {
-    sol::function fn = lua_[fn_name.c_str()];
-    fn(convert(args)...);
+    sol::protected_function_result function_result;
+    try {
+      sol::protected_function fn = lua_[fn_name.c_str()];
+      function_result = fn(convert(args)...);
+    } catch (const std::exception& e) {
+      throw minifi::script::ScriptException(e.what());
+    }
+    if (!function_result.valid()) {
+      sol::error err = function_result;
+      throw minifi::script::ScriptException(err.what());
+    }
   }
 
   class TriggerSession {
diff --git a/extensions/script/python/ExecutePythonProcessor.cpp 
b/extensions/script/python/ExecutePythonProcessor.cpp
index e8813b0..62e8c23 100644
--- a/extensions/script/python/ExecutePythonProcessor.cpp
+++ b/extensions/script/python/ExecutePythonProcessor.cpp
@@ -127,24 +127,14 @@ void ExecutePythonProcessor::onSchedule(const 
std::shared_ptr<core::ProcessConte
 }
 
 void ExecutePythonProcessor::onTrigger(const 
std::shared_ptr<core::ProcessContext> &context, const 
std::shared_ptr<core::ProcessSession> &session) {
-  try {
-    auto engine = getScriptEngine();
-    reloadScriptIfUsingScriptFileProperty(*engine);
-    if (script_to_exec_.empty()) {
-      throw std::runtime_error("Neither Script Body nor Script File is 
available to execute");
-    }
-
-    engine->onTrigger(context, session);
-    handleEngineNoLongerInUse(std::move(engine));
-  }
-  catch (const std::exception &exception) {
-    logger_->log_error("Caught Exception: %s", exception.what());
-    this->yield();
-  }
-  catch (...) {
-    logger_->log_error("Caught Exception");
-    this->yield();
+  auto engine = getScriptEngine();
+  reloadScriptIfUsingScriptFileProperty(*engine);
+  if (script_to_exec_.empty()) {
+    throw std::runtime_error("Neither Script Body nor Script File is available 
to execute");
   }
+
+  engine->onTrigger(context, session);
+  handleEngineNoLongerInUse(std::move(engine));
 }
 
 // TODO(hunyadi): This is potentially not what we want. See 
https://issues.apache.org/jira/browse/MINIFICPP-1222
diff --git a/extensions/script/python/PythonScriptEngine.h 
b/extensions/script/python/PythonScriptEngine.h
index 96a1195..4337f52 100644
--- a/extensions/script/python/PythonScriptEngine.h
+++ b/extensions/script/python/PythonScriptEngine.h
@@ -57,7 +57,7 @@ struct Interpreter {
 
 Interpreter *getInterpreter();
 
-class PythonScriptEngine : public script::ScriptEngine {
+class __attribute__((visibility("default"))) PythonScriptEngine : public 
script::ScriptEngine {
  public:
   PythonScriptEngine();
   virtual ~PythonScriptEngine() {
diff --git a/libminifi/test/script-tests/CMakeLists.txt 
b/extensions/script/tests/CMakeLists.txt
similarity index 82%
rename from libminifi/test/script-tests/CMakeLists.txt
rename to extensions/script/tests/CMakeLists.txt
index b616ae6..3319763 100644
--- a/libminifi/test/script-tests/CMakeLists.txt
+++ b/extensions/script/tests/CMakeLists.txt
@@ -18,28 +18,29 @@
 #
 
 if (NOT DISABLE_PYTHON_SCRIPTING)
-       file(GLOB EXECUTESCRIPT_PYTHON_INTEGRATION_TESTS  
"TestExecuteScriptProcessorWithPythonScript.cpp")
+       file(GLOB EXECUTESCRIPT_PYTHON_TESTS  
"TestExecuteScriptProcessorWithPythonScript.cpp" "PythonScriptEngineTests.cpp")
        file(GLOB EXECUTEPYTHONPROCESSOR_UNIT_TESTS  
"ExecutePythonProcessorTests.cpp")
        file(GLOB PY_SOURCES  "python/*.cpp")
        find_package(PythonLibs 3.5)
        if (NOT PYTHONLIBS_FOUND)
                find_package(PythonLibs 3.0 REQUIRED)
        endif()
-       file(COPY 
"${CMAKE_SOURCE_DIR}/libminifi/test/script-tests/test_scripts" DESTINATION 
"${CMAKE_CURRENT_BINARY_DIR}/")
+       file(COPY "${CMAKE_SOURCE_DIR}/extensions/script/tests/test_scripts" 
DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/")
 endif()
 
 if (ENABLE_LUA_SCRIPTING)
-       file(GLOB EXECUTESCRIPT_LUA_INTEGRATION_TESTS  "Lua*.cpp")
+       file(GLOB EXECUTESCRIPT_LUA_TESTS 
"TestExecuteScriptProcessorWithLuaScript.cpp" "LuaScriptEngineTests.cpp")
 endif()
 
 SET(EXTENSIONS_TEST_COUNT 0)
 
-FOREACH(testfile ${EXECUTESCRIPT_PYTHON_INTEGRATION_TESTS})
+FOREACH(testfile ${EXECUTESCRIPT_PYTHON_TESTS})
        get_filename_component(testfilename "${testfile}" NAME_WE)
        add_executable("${testfilename}" "${testfile}")
        target_include_directories(${testfilename} PRIVATE BEFORE 
"${CMAKE_SOURCE_DIR}/extensions/script")
        target_include_directories(${testfilename} PRIVATE BEFORE 
"${CMAKE_SOURCE_DIR}/extensions/standard-processors")
        target_include_directories(${testfilename} PRIVATE BEFORE 
"${CMAKE_SOURCE_DIR}/extensions/script/python")
+       target_include_directories(${testfilename} PRIVATE BEFORE 
"${CMAKE_SOURCE_DIR}/libminifi/test/")
        target_include_directories(${testfilename} SYSTEM PRIVATE BEFORE 
"${CMAKE_SOURCE_DIR}/thirdparty/pybind11/include")
        add_definitions(-DPYTHON_SUPPORT)
        target_link_libraries(${testfilename} minifi-script-extensions)
@@ -62,6 +63,7 @@ FOREACH(testfile ${EXECUTEPYTHONPROCESSOR_UNIT_TESTS})
        target_include_directories(${testfilename} PRIVATE BEFORE 
"${CMAKE_SOURCE_DIR}/extensions/script")
        target_include_directories(${testfilename} PRIVATE BEFORE 
"${CMAKE_SOURCE_DIR}/extensions/standard-processors")
        target_include_directories(${testfilename} PRIVATE BEFORE 
"${CMAKE_SOURCE_DIR}/extensions/script/python")
+       target_include_directories(${testfilename} PRIVATE BEFORE 
"${CMAKE_SOURCE_DIR}/libminifi/test/")
        target_include_directories(${testfilename} SYSTEM PRIVATE BEFORE 
"${CMAKE_SOURCE_DIR}/thirdparty/pybind11/include")
 
        target_link_libraries(${testfilename} minifi-script-extensions)
@@ -73,13 +75,15 @@ FOREACH(testfile ${EXECUTEPYTHONPROCESSOR_UNIT_TESTS})
        MATH(EXPR EXTENSIONS_TEST_COUNT "${EXTENSIONS_TEST_COUNT}+1")
 ENDFOREACH()
 
-FOREACH(testfile ${EXECUTESCRIPT_LUA_INTEGRATION_TESTS})
+FOREACH(testfile ${EXECUTESCRIPT_LUA_TESTS})
        get_filename_component(testfilename "${testfile}" NAME_WE)
        add_executable("${testfilename}" "${testfile}")
        target_include_directories(${testfilename} PRIVATE BEFORE 
"${CMAKE_SOURCE_DIR}/extensions/script")
        target_include_directories(${testfilename} PRIVATE BEFORE 
"${CMAKE_SOURCE_DIR}/extensions/standard-processors")
        target_include_directories(${testfilename} PRIVATE BEFORE 
"${CMAKE_SOURCE_DIR}/extensions/script/lua")
+       target_include_directories(${testfilename} PRIVATE BEFORE 
"${CMAKE_SOURCE_DIR}/libminifi/test/")
        target_include_directories(${testfilename} SYSTEM PRIVATE BEFORE 
"${SOL2_INCLUDE_DIR}")
+       target_include_directories(${testfilename} PRIVATE BEFORE 
${LUA_INCLUDE_DIR})
        add_definitions(-DLUA_SUPPORT)
        target_link_libraries(${testfilename} minifi-script-extensions)
        target_link_libraries(${testfilename} minifi-standard-processors)
@@ -89,4 +93,4 @@ FOREACH(testfile ${EXECUTESCRIPT_LUA_INTEGRATION_TESTS})
        add_test(NAME "${testfilename}" COMMAND "${testfilename}" 
WORKING_DIRECTORY ${TEST_DIR})
 ENDFOREACH()
 
-message("-- Finished building ${EXTENSIONS_TEST_COUNT} Python ExecuteScript 
related test file(s)...")
+message("-- Finished building ${EXTENSIONS_TEST_COUNT} ExecuteScript related 
test file(s)...")
diff --git a/libminifi/test/script-tests/ExecutePythonProcessorTests.cpp 
b/extensions/script/tests/ExecutePythonProcessorTests.cpp
similarity index 99%
rename from libminifi/test/script-tests/ExecutePythonProcessorTests.cpp
rename to extensions/script/tests/ExecutePythonProcessorTests.cpp
index 3416e62..83d7991 100644
--- a/libminifi/test/script-tests/ExecutePythonProcessorTests.cpp
+++ b/extensions/script/tests/ExecutePythonProcessorTests.cpp
@@ -20,7 +20,7 @@
 #include <string>
 #include <set>
 
-#include "../TestBase.h"
+#include "TestBase.h"
 
 #include "processors/GetFile.h"
 #include "python/ExecutePythonProcessor.h"
diff --git a/extensions/script/tests/LuaScriptEngineTests.cpp 
b/extensions/script/tests/LuaScriptEngineTests.cpp
new file mode 100644
index 0000000..27716fd
--- /dev/null
+++ b/extensions/script/tests/LuaScriptEngineTests.cpp
@@ -0,0 +1,46 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TestBase.h"
+#include "Utils.h"
+#include "lua/LuaScriptEngine.h"
+
+using ScriptException = org::apache::nifi::minifi::script::ScriptException;
+using LuaScriptEngine = org::apache::nifi::minifi::lua::LuaScriptEngine;
+
+TEST_CASE("LuaScriptEngine errors during eval", "[luascriptengineeval]") {
+  LuaScriptEngine engine;
+  REQUIRE_NOTHROW(engine.eval("print('foo')"));
+  // The exception message comes from the lua engine
+  REQUIRE_THROWS_MATCHES(engine.eval("shout('foo')"), ScriptException, 
ExceptionSubStringMatcher<ScriptException>({"global 'shout' is not callable (a 
nil value)", "attempt to call a nil value"}));
+}
+
+TEST_CASE("LuaScriptEngine errors during call", "[luascriptenginecall]") {
+  LuaScriptEngine engine;
+  REQUIRE_NOTHROW(engine.eval(R"(
+    function foo()
+      print('foo')
+    end
+
+    function bar()
+      shout('bar')
+    end
+  )"));
+  REQUIRE_NOTHROW(engine.call("foo"));
+  // The exception message comes from the lua engine
+  REQUIRE_THROWS_MATCHES(engine.call("bar"), ScriptException, 
ExceptionSubStringMatcher<ScriptException>({"global 'shout' is not callable (a 
nil value)", "attempt to call a nil value"}));
+}
diff --git a/extensions/script/tests/PythonScriptEngineTests.cpp 
b/extensions/script/tests/PythonScriptEngineTests.cpp
new file mode 100644
index 0000000..37343d7
--- /dev/null
+++ b/extensions/script/tests/PythonScriptEngineTests.cpp
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TestBase.h"
+#include "Utils.h"
+#include "python/PythonScriptEngine.h"
+
+using PythonScriptEngine = 
org::apache::nifi::minifi::python::PythonScriptEngine;
+using ScriptException = org::apache::nifi::minifi::script::ScriptException;
+
+
+TEST_CASE("PythonScriptEngine errors during eval", "[pythonscriptengineeval]") 
{
+  PythonScriptEngine engine;
+  REQUIRE_NOTHROW(engine.eval("print('foo')"));
+  REQUIRE_THROWS_MATCHES(engine.eval("shout('foo')"), ScriptException, 
ExceptionSubStringMatcher<ScriptException>({"name 'shout' is not defined"}));
+}
+
+TEST_CASE("PythonScriptEngine errors during call", "[luascriptenginecall]") {
+  PythonScriptEngine engine;
+  REQUIRE_NOTHROW(engine.eval(R"(
+    def foo():
+      print('foo')
+
+    def bar():
+      shout('bar')
+
+  )"));
+  REQUIRE_NOTHROW(engine.call("foo"));
+  REQUIRE_THROWS_MATCHES(engine.call("bar"), ScriptException, 
ExceptionSubStringMatcher<ScriptException>({"name 'shout' is not defined"}));
+}
diff --git 
a/libminifi/test/script-tests/TestExecuteScriptProcessorWithLuaScript.cpp 
b/extensions/script/tests/TestExecuteScriptProcessorWithLuaScript.cpp
similarity index 83%
rename from 
libminifi/test/script-tests/TestExecuteScriptProcessorWithLuaScript.cpp
rename to extensions/script/tests/TestExecuteScriptProcessorWithLuaScript.cpp
index 99971eb..5667858 100644
--- a/libminifi/test/script-tests/TestExecuteScriptProcessorWithLuaScript.cpp
+++ b/extensions/script/tests/TestExecuteScriptProcessorWithLuaScript.cpp
@@ -20,7 +20,7 @@
 #include <string>
 #include <set>
 
-#include "../TestBase.h"
+#include "TestBase.h"
 
 #include <ExecuteScript.h>
 #include "processors/LogAttribute.h"
@@ -43,15 +43,15 @@ TEST_CASE("Lua: Test Log", "[executescriptLuaLog]") { // 
NOLINT
                                           core::Relationship("success", 
"description"),
                                           true);
 
-  plan->setProperty(executeScript, 
processors::ExecuteScript::ScriptEngine.getName(), "lua");
-  plan->setProperty(executeScript, 
processors::ExecuteScript::ScriptBody.getName(), R"(
+  plan->setProperty(executeScript, 
minifi::processors::ExecuteScript::ScriptEngine.getName(), "lua");
+  plan->setProperty(executeScript, 
minifi::processors::ExecuteScript::ScriptBody.getName(), R"(
     function onTrigger(context, session)
       log:info('hello from lua')
     end
   )");
 
   auto getFileDir = testController.createTempDirectory();
-  plan->setProperty(getFile, processors::GetFile::Directory.getName(), 
getFileDir);
+  plan->setProperty(getFile, minifi::processors::GetFile::Directory.getName(), 
getFileDir);
 
   std::fstream file;
   std::stringstream ss;
@@ -90,12 +90,12 @@ TEST_CASE("Lua: Test Read File", "[executescriptLuaRead]") 
{ // NOLINT
                                           true);
   auto putFile = plan->addProcessor("PutFile", "putFile", 
core::Relationship("success", "description"), true);
 
-  plan->setProperty(executeScript, 
processors::ExecuteScript::ScriptEngine.getName(), "lua");
-  plan->setProperty(executeScript, 
processors::ExecuteScript::ScriptBody.getName(), R"(
+  plan->setProperty(executeScript, 
minifi::processors::ExecuteScript::ScriptEngine.getName(), "lua");
+  plan->setProperty(executeScript, 
minifi::processors::ExecuteScript::ScriptBody.getName(), R"(
     read_callback = {}
 
     function read_callback.process(self, input_stream)
-        content = input_stream:read()
+        content = input_stream:read(0)
         log:info('file content: ' .. content)
         return #content
     end
@@ -112,10 +112,10 @@ TEST_CASE("Lua: Test Read File", 
"[executescriptLuaRead]") { // NOLINT
   )");
 
   auto getFileDir = testController.createTempDirectory();
-  plan->setProperty(getFile, processors::GetFile::Directory.getName(), 
getFileDir);
+  plan->setProperty(getFile, minifi::processors::GetFile::Directory.getName(), 
getFileDir);
 
   auto putFileDir = testController.createTempDirectory();
-  plan->setProperty(putFile, processors::PutFile::Directory.getName(), 
putFileDir);
+  plan->setProperty(putFile, minifi::processors::PutFile::Directory.getName(), 
putFileDir);
 
   testController.runSession(plan, false);
 
@@ -178,8 +178,8 @@ TEST_CASE("Lua: Test Write File", 
"[executescriptLuaWrite]") { // NOLINT
                                           true);
   auto putFile = plan->addProcessor("PutFile", "putFile", 
core::Relationship("success", "description"), true);
 
-  plan->setProperty(executeScript, 
processors::ExecuteScript::ScriptEngine.getName(), "lua");
-  plan->setProperty(executeScript, 
processors::ExecuteScript::ScriptBody.getName(), R"(
+  plan->setProperty(executeScript, 
minifi::processors::ExecuteScript::ScriptEngine.getName(), "lua");
+  plan->setProperty(executeScript, 
minifi::processors::ExecuteScript::ScriptBody.getName(), R"(
     write_callback = {}
 
     function write_callback.process(self, output_stream)
@@ -200,10 +200,10 @@ TEST_CASE("Lua: Test Write File", 
"[executescriptLuaWrite]") { // NOLINT
   )");
 
   auto getFileDir = testController.createTempDirectory();
-  plan->setProperty(getFile, processors::GetFile::Directory.getName(), 
getFileDir);
+  plan->setProperty(getFile, minifi::processors::GetFile::Directory.getName(), 
getFileDir);
 
   auto putFileDir = testController.createTempDirectory();
-  plan->setProperty(putFile, processors::PutFile::Directory.getName(), 
putFileDir);
+  plan->setProperty(putFile, minifi::processors::PutFile::Directory.getName(), 
putFileDir);
 
   testController.runSession(plan, false);
 
@@ -263,8 +263,8 @@ TEST_CASE("Lua: Test Update Attribute", 
"[executescriptLuaUpdateAttribute]") { /
                                          core::Relationship("success", 
"description"),
                                          true);
 
-  plan->setProperty(executeScript, 
processors::ExecuteScript::ScriptEngine.getName(), "lua");
-  plan->setProperty(executeScript, 
processors::ExecuteScript::ScriptBody.getName(), R"(
+  plan->setProperty(executeScript, 
minifi::processors::ExecuteScript::ScriptEngine.getName(), "lua");
+  plan->setProperty(executeScript, 
minifi::processors::ExecuteScript::ScriptBody.getName(), R"(
     function onTrigger(context, session)
       flow_file = session:get()
 
@@ -273,14 +273,14 @@ TEST_CASE("Lua: Test Update Attribute", 
"[executescriptLuaUpdateAttribute]") { /
         flow_file:addAttribute('test_attr', '1')
         attr = flow_file:getAttribute('test_attr')
         log:info('got flow file attr \'test_attr\': ' .. attr)
-        flow_file:updateAttribute('test_attr', attr + 1)
+        flow_file:updateAttribute('test_attr', tostring(attr + 1))
         session:transfer(flow_file, REL_SUCCESS)
       end
     end
   )");
 
   auto getFileDir = testController.createTempDirectory();
-  plan->setProperty(getFile, processors::GetFile::Directory.getName(), 
getFileDir);
+  plan->setProperty(getFile, minifi::processors::GetFile::Directory.getName(), 
getFileDir);
 
   std::fstream file;
   std::stringstream ss;
@@ -290,9 +290,9 @@ TEST_CASE("Lua: Test Update Attribute", 
"[executescriptLuaUpdateAttribute]") { /
   file.close();
   plan->reset();
 
-  testController.runSession(plan, false);
-  testController.runSession(plan, false);
-  testController.runSession(plan, false);
+  REQUIRE_NOTHROW(testController.runSession(plan, false));
+  REQUIRE_NOTHROW(testController.runSession(plan, false));
+  REQUIRE_NOTHROW(testController.runSession(plan, false));
 
   REQUIRE(LogTestController::getInstance().contains("key:test_attr value:2"));
 
@@ -311,8 +311,8 @@ TEST_CASE("Lua: Test Create", "[executescriptLuaCreate]") { 
// NOLINT
   auto executeScript = plan->addProcessor("ExecuteScript",
                                           "executeScript");
 
-  plan->setProperty(executeScript, 
processors::ExecuteScript::ScriptEngine.getName(), "lua");
-  plan->setProperty(executeScript, 
processors::ExecuteScript::ScriptBody.getName(), R"(
+  plan->setProperty(executeScript, 
minifi::processors::ExecuteScript::ScriptEngine.getName(), "lua");
+  plan->setProperty(executeScript, 
minifi::processors::ExecuteScript::ScriptBody.getName(), R"(
     function onTrigger(context, session)
       flow_file = session:create(nil)
 
@@ -344,8 +344,8 @@ TEST_CASE("Lua: Test Require", "[executescriptLuaRequire]") 
{ // NOLINT
   auto executeScript = plan->addProcessor("ExecuteScript",
                                           "executeScript");
 
-  plan->setProperty(executeScript, 
processors::ExecuteScript::ScriptEngine.getName(), "lua");
-  plan->setProperty(executeScript, 
processors::ExecuteScript::ScriptBody.getName(), R"(
+  plan->setProperty(executeScript, 
minifi::processors::ExecuteScript::ScriptEngine.getName(), "lua");
+  plan->setProperty(executeScript, 
minifi::processors::ExecuteScript::ScriptBody.getName(), R"(
     require 'os'
     require 'coroutine'
     require 'math'
@@ -355,11 +355,14 @@ TEST_CASE("Lua: Test Require", 
"[executescriptLuaRequire]") { // NOLINT
     require 'package'
 
     log:info('OK')
+
+    function onTrigger(context, session)
+    end
   )");
 
   plan->reset();
 
-  testController.runSession(plan, false);
+  REQUIRE_NOTHROW(testController.runSession(plan, false));
 
   REQUIRE(LogTestController::getInstance().contains("[info] OK"));
 
diff --git 
a/libminifi/test/script-tests/TestExecuteScriptProcessorWithPythonScript.cpp 
b/extensions/script/tests/TestExecuteScriptProcessorWithPythonScript.cpp
similarity index 99%
rename from 
libminifi/test/script-tests/TestExecuteScriptProcessorWithPythonScript.cpp
rename to extensions/script/tests/TestExecuteScriptProcessorWithPythonScript.cpp
index 366b681..1124745 100644
--- a/libminifi/test/script-tests/TestExecuteScriptProcessorWithPythonScript.cpp
+++ b/extensions/script/tests/TestExecuteScriptProcessorWithPythonScript.cpp
@@ -20,7 +20,7 @@
 #include <string>
 #include <set>
 
-#include "../TestBase.h"
+#include "TestBase.h"
 
 #include <ExecuteScript.h>
 #include "processors/LogAttribute.h"
diff --git 
a/libminifi/test/script-tests/test_scripts/non_transferring_processor.py 
b/extensions/script/tests/test_scripts/non_transferring_processor.py
similarity index 100%
rename from 
libminifi/test/script-tests/test_scripts/non_transferring_processor.py
rename to extensions/script/tests/test_scripts/non_transferring_processor.py
diff --git 
a/libminifi/test/script-tests/test_scripts/passthrough_processor_transfering_to_failure.py
 
b/extensions/script/tests/test_scripts/passthrough_processor_transfering_to_failure.py
similarity index 100%
rename from 
libminifi/test/script-tests/test_scripts/passthrough_processor_transfering_to_failure.py
rename to 
extensions/script/tests/test_scripts/passthrough_processor_transfering_to_failure.py
diff --git 
a/libminifi/test/script-tests/test_scripts/passthrough_processor_transfering_to_success.py
 
b/extensions/script/tests/test_scripts/passthrough_processor_transfering_to_success.py
similarity index 100%
rename from 
libminifi/test/script-tests/test_scripts/passthrough_processor_transfering_to_success.py
rename to 
extensions/script/tests/test_scripts/passthrough_processor_transfering_to_success.py
diff --git a/libminifi/test/script-tests/test_scripts/stateful_processor.py 
b/extensions/script/tests/test_scripts/stateful_processor.py
similarity index 100%
rename from libminifi/test/script-tests/test_scripts/stateful_processor.py
rename to extensions/script/tests/test_scripts/stateful_processor.py
diff --git a/libminifi/test/Utils.h b/libminifi/test/Utils.h
index c18d8f1..0864b3b 100644
--- a/libminifi/test/Utils.h
+++ b/libminifi/test/Utils.h
@@ -18,6 +18,7 @@
 
 #include <string>
 #include <utility>
+#include <vector>
 
 #include "rapidjson/document.h"
 
@@ -66,3 +67,23 @@ void verifyJSON(const std::string& actual_str, const 
std::string& expected_str,
     matchJSON(actual, expected);
   }
 }
+
+template<typename T>
+class ExceptionSubStringMatcher : public Catch::MatcherBase<T> {
+ public:
+  explicit ExceptionSubStringMatcher(std::vector<std::string> 
exception_substr) :
+      possible_exception_substrs_(std::move(exception_substr)) {}
+
+  bool match(T const& script_exception) const override {
+    for (auto& possible_exception_substr : possible_exception_substrs_) {
+      if (std::string(script_exception.what()).find(possible_exception_substr) 
!= std::string::npos)
+        return true;
+    }
+    return false;
+  }
+
+  std::string describe() const override { return "Checks whether the exception 
message contains at least one of the provided exception substrings"; }
+
+ private:
+  std::vector<std::string> possible_exception_substrs_;
+};

Reply via email to