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

szaszm 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 69a024228 MINIFICPP-2905 Run valgrind on all unit tests (#2282)
69a024228 is described below

commit 69a02422803151c016e55de0b792c00e55843546
Author: Ferenc Gerlits <[email protected]>
AuthorDate: Thu Oct 22 11:37:47 2026 +0200

    MINIFICPP-2905 Run valgrind on all unit tests (#2282)
    
    * MINIFICPP-2905 Run valgrind on all unit tests
    
    Remove the "memchecked" tag from the unit tests and run valgrind on
    almost all tests instead of only the tagged tests.
    Also:
    * increase timeouts by a factor of 50 when running under Valgrind
      (this affects failures only; on success, the verifier returns early)
    * skip libminifi/test/integration tests: these are too dependent on timing
      and become flaky under valgrind
    * skip some more tests which don't work well under valgrind
    * add/expand suppressions for some known non-issues
    
    * fix typo: 'phd' to 'pdh'
    
    Co-authored-by: Copilot Autofix powered by AI 
<[email protected]>
    
    * workaround for GCC 15/16 bug 
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=124584
    
    asio::experimental::awaitable_operators::operator|| is broken due to this 
bug,
    so use a (more verbose) hand-rolled alternative
    
    * narrow the valgrind suppression
    
    * move the type from comment to code
    
    * revert the name of the LogCompressorSink valgrind suppression rule
    
    ---------
    
    Co-authored-by: Copilot Autofix powered by AI 
<[email protected]>
---
 .github/workflows/memcheck_ci.yml                  | 17 +++++----
 cmake/valgrind.supp                                | 44 ++++++++++++++++++++++
 core-framework/include/utils/net/AsioCoro.h        | 18 ++++++---
 extensions/azure/tests/CMakeLists.txt              |  2 +-
 extensions/bustache/tests/CMakeLists.txt           |  2 +-
 extensions/civetweb/tests/C2VerifyServeResults.cpp |  2 +-
 .../civetweb/tests/HttpPostIntegrationTest.cpp     |  2 +-
 extensions/elasticsearch/tests/CMakeLists.txt      |  2 +-
 extensions/execute-process/test/CMakeLists.txt     |  2 +-
 extensions/grafana-loki/tests/CMakeLists.txt       |  2 +-
 extensions/kafka/tests/CMakeLists.txt              |  2 +-
 extensions/kubernetes/tests/CMakeLists.txt         |  2 +-
 extensions/llamacpp/tests/CMakeLists.txt           |  2 +-
 extensions/lua/tests/CMakeLists.txt                |  2 +-
 extensions/mqtt/tests/CMakeLists.txt               |  2 +-
 extensions/opc/tests/CMakeLists.txt                |  2 +-
 extensions/pdh/tests/CMakeLists.txt                |  2 +-
 extensions/procfs/tests/CMakeLists.txt             |  2 +-
 extensions/prometheus/tests/CMakeLists.txt         |  2 +-
 extensions/rocksdb-repos/tests/RepoTests.cpp       |  5 +++
 extensions/stable-api-testing/tests/CMakeLists.txt |  2 +-
 .../tests/integration/ProcessGroupTest.cpp         |  3 +-
 .../tests/unit/AttributeRollingWindowTests.cpp     | 12 ++++--
 .../standard-processors/tests/unit/GetTCPTests.cpp |  6 ++-
 .../tests/unit/ProcessorTests.cpp                  |  2 +-
 .../standard-processors/tests/unit/PutTCPTests.cpp |  8 ++--
 libminifi/test/flow-tests/FlowControllerTests.cpp  | 17 ++-------
 libminifi/test/integration/CMakeLists.txt          |  2 +
 .../test/libtest/integration/HTTPIntegrationBase.h | 12 +++---
 .../test/libtest/integration/IntegrationBase.cpp   |  1 +
 .../test/libtest/integration/IntegrationBase.h     |  7 ++--
 .../libtest/unit/SingleProcessorTestController.cpp |  3 +-
 libminifi/test/libtest/unit/TestUtils.cpp          | 11 +++++-
 libminifi/test/libtest/unit/TestUtils.h            | 13 ++++++-
 libminifi/test/unit/FlowFileQueueTests.cpp         | 26 ++++++-------
 libminifi/test/unit/MemoryUsageTest.cpp            |  5 +++
 libminifi/test/unit/MinifiConcurrentQueueTests.cpp | 23 ++++++++---
 37 files changed, 183 insertions(+), 86 deletions(-)

diff --git a/.github/workflows/memcheck_ci.yml 
b/.github/workflows/memcheck_ci.yml
index 6fc127a85..76e4201c2 100644
--- a/.github/workflows/memcheck_ci.yml
+++ b/.github/workflows/memcheck_ci.yml
@@ -17,8 +17,8 @@ env:
   CCACHE_DIR: ${{ GITHUB.WORKSPACE }}/.ccache
 jobs:
   valgrind:
-    name: "Valgrind on ubuntu-24.04"
-    runs-on: ubuntu-24.04
+    name: "Valgrind"
+    runs-on: ubuntu-26.04
     timeout-minutes: 300
     steps:
       - id: checkout
@@ -27,10 +27,10 @@ jobs:
         uses: actions/cache/restore@v5
         with:
           path: ${{ env.CCACHE_DIR }}
-          key: ubuntu-24.04-valgrind-ccache-${{github.ref}}-${{github.sha}}
+          key: ubuntu-26.04-valgrind-ccache-${{github.ref}}-${{github.sha}}
           restore-keys: |
-            ubuntu-24.04-valgrind-ccache-${{github.ref}}-
-            ubuntu-24.04-valgrind-ccache-refs/heads/main-
+            ubuntu-26.04-valgrind-ccache-${{github.ref}}-
+            ubuntu-26.04-valgrind-ccache-refs/heads/main-
       - id: install_deps
         run: |
           sudo apt update
@@ -48,16 +48,17 @@ jobs:
         if: always()
         with:
           path: ${{ env.CCACHE_DIR }}
-          key: ubuntu-24.04-valgrind-ccache-${{github.ref}}-${{github.sha}}
+          key: ubuntu-26.04-valgrind-ccache-${{github.ref}}-${{github.sha}}
       - name: test
         id: test
         run: |
           # Set core file size limit to unlimited
           ulimit -c unlimited
-          ctest -j$(nproc) -L memchecked -T memcheck
+          ctest -j$(nproc) -T memcheck -LE integration --output-on-failure
         working-directory: build
       - name: check valgrind output
         id: check
+        if: always()
         run: |
           nonempty_valgrind_outputs=$(find Testing/Temporary -maxdepth 1 -name 
'MemoryChecker.*.log' -size +0 -print)
           if [ -n "$nonempty_valgrind_outputs" ]; then
@@ -76,7 +77,7 @@ jobs:
             build/Testing/Temporary/LastDynamicAnalysis_*.log
           if-no-files-found: ignore
   address-sanitizer:
-    name: "AddressSanitizer+LeakSanitizer on ubuntu 26.04"
+    name: "AddressSanitizer+LeakSanitizer"
     runs-on: ubuntu-26.04
     timeout-minutes: 300
     steps:
diff --git a/cmake/valgrind.supp b/cmake/valgrind.supp
index 9beb95743..184303afc 100644
--- a/cmake/valgrind.supp
+++ b/cmake/valgrind.supp
@@ -45,6 +45,8 @@
    match-leak-kinds: possible
    fun:calloc
    ...
+   fun:_dl_allocate_tls
+   ...
    fun:pthread_create*
    ...
    fun:*LogCompressorSink*
@@ -91,6 +93,24 @@
    ...
 }
 
+# glibc lazily allocates a module's Thread-Local-Storage block (a DTV entry) 
the first time a
+# thread touches a thread_local in a dynamically-loaded library (e.g. the Lua 
script extension):
+# __tls_get_addr -> tls_get_addr_tail -> allocate_and_init -> 
allocate_dtv_entry. The block is
+# owned by the dynamic linker's DTV and freed by the OS at exit; valgrind only 
holds an interior
+# pointer, so it is flagged "possibly lost". Sibling path to 
possibly_lost_tls_dtv_growth (which
+# matches DTV *resize* instead). Never a real leak.
+{
+   possibly_lost_tls_dtv_entry_allocation
+   Memcheck:Leak
+   match-leak-kinds: possible
+   fun:malloc
+   ...
+   fun:allocate_dtv_entry
+   ...
+   fun:__tls_get_addr
+   ...
+}
+
 # glibc grows the Thread-Local-Storage descriptor vector (DTV) the first time a
 # thread touches a dynamically-loaded TLS variable. The DTV block is kept for
 # the thread's lifetime and freed by the OS at exit; the growth happens only
@@ -183,3 +203,27 @@
    fun:*CollectionScope*
    ...
 }
+
+# Suppress a "Conditional jump or move depends on uninitialised value(s)" error
+# in zlib: this is intentional optimization, with the comment "prev[n] is 
garbage
+# but its value will never be used" at deflate.c:204.
+{
+   zlib_slide_hash_uninitialised_prev
+   Memcheck:Cond
+   fun:slide_hash
+   fun:fill_window
+   ...
+}
+
+# "Conditional jump or move depends on uninitialised value(s)" inside the 
libstdc++
+# std::from_chars<int> that is inlined into Cron's fromChars<>/parse<year>
+# (utils/Cron.cpp). The input std::string is fully initialised and fromChars 
value-
+# initialises its output; this is a known valgrind imprecision on optimized 
integer
+# parsing, not a real defect.
+{
+   cron_parse_year_fromchars_uninitialised
+   Memcheck:Cond
+   fun:*parse*year*
+   fun:*Cron*
+   ...
+}
diff --git a/core-framework/include/utils/net/AsioCoro.h 
b/core-framework/include/utils/net/AsioCoro.h
index b18087268..4b38a428a 100644
--- a/core-framework/include/utils/net/AsioCoro.h
+++ b/core-framework/include/utils/net/AsioCoro.h
@@ -28,7 +28,9 @@
 #include "asio/steady_timer.hpp"
 #include "asio/this_coro.hpp"
 #include "asio/use_awaitable.hpp"
-#include "asio/experimental/awaitable_operators.hpp"
+#include "asio/co_spawn.hpp"
+#include "asio/deferred.hpp"
+#include "asio/experimental/parallel_group.hpp"
 #include "asio/as_tuple.hpp"
 
 namespace org::apache::nifi::minifi::utils::net {
@@ -57,13 +59,19 @@ inline asio::awaitable<void> 
async_wait(std::chrono::steady_clock::duration dura
 template<class... Types>
 asio::awaitable<std::tuple<std::error_code, Types...>> 
asyncOperationWithTimeout(asio::awaitable<std::tuple<std::error_code, 
Types...>>&& async_operation,
     std::chrono::steady_clock::duration timeout_duration) {
-  using asio::experimental::awaitable_operators::operator||;
-  auto operation_result = co_await(std::move(async_operation) || 
async_wait(timeout_duration));  // NOLINT
-  if (operation_result.index() == 1) {
+  auto executor = co_await asio::this_coro::executor;  // 
NOLINT(clang-analyzer-core.CallAndMessage)
+  std::tuple<std::array<std::size_t, 2>, std::exception_ptr, 
std::tuple<std::error_code, Types...>, std::exception_ptr> 
parallel_group_result{
+      co_await asio::experimental::make_parallel_group(  // 
NOLINT(clang-analyzer-core.CallAndMessage)
+          asio::co_spawn(executor, std::move(async_operation), asio::deferred),
+          asio::co_spawn(executor, async_wait(timeout_duration), 
asio::deferred))
+      .async_wait(asio::experimental::wait_for_one_success(), 
asio::use_awaitable)
+  };
+  auto index_of_operation_which_finished_first = 
std::get<0>(parallel_group_result)[0];  // 0 = async_operation, 1 = 
async_wait(timeout_duration)
+  if (index_of_operation_which_finished_first != 0) {
     std::tuple<std::error_code, Types...> result;
     std::get<0>(result) = asio::error::timed_out;
     co_return result;
   }
-  co_return std::get<0>(operation_result);
+  co_return std::move(std::get<2>(parallel_group_result));
 }
 }  // namespace org::apache::nifi::minifi::utils::net
diff --git a/extensions/azure/tests/CMakeLists.txt 
b/extensions/azure/tests/CMakeLists.txt
index 5eac1ab38..9c26b57d2 100644
--- a/extensions/azure/tests/CMakeLists.txt
+++ b/extensions/azure/tests/CMakeLists.txt
@@ -34,6 +34,6 @@ FOREACH(testfile ${AZURE_INTEGRATION_TESTS})
     target_link_libraries(${testfilename} minifi-standard-processors)
     MATH(EXPR AZURE_TEST_COUNT "${AZURE_TEST_COUNT}+1")
     add_test(NAME "${testfilename}" COMMAND "${testfilename}" 
WORKING_DIRECTORY ${TEST_DIR})
-    set_tests_properties("${testfilename}" PROPERTIES LABELS 
"azure;memchecked")
+    set_tests_properties("${testfilename}" PROPERTIES LABELS "azure")
 ENDFOREACH()
 message("-- Finished building ${AZURE_TEST_COUNT} Azure related test 
file(s)...")
diff --git a/extensions/bustache/tests/CMakeLists.txt 
b/extensions/bustache/tests/CMakeLists.txt
index 2a87b0ef0..8fd537e16 100644
--- a/extensions/bustache/tests/CMakeLists.txt
+++ b/extensions/bustache/tests/CMakeLists.txt
@@ -34,6 +34,6 @@ FOREACH(testfile ${BUSTACHE_INTEGRATION_TESTS})
     target_link_libraries(${testfilename} Catch2WithMain)
     MATH(EXPR BUSTACHE-EXTENSIONS_TEST_COUNT 
"${BUSTACHE-EXTENSIONS_TEST_COUNT}+1")
     add_test(NAME "${testfilename}" COMMAND "${testfilename}" 
WORKING_DIRECTORY ${TEST_DIR})
-    set_tests_properties("${testfilename}" PROPERTIES LABELS 
"bustache;memchecked")
+    set_tests_properties("${testfilename}" PROPERTIES LABELS "bustache")
 ENDFOREACH()
 message("-- Finished building ${BUSTACHE-EXTENSIONS_TEST_COUNT} Bustache 
related test file(s)...")
diff --git a/extensions/civetweb/tests/C2VerifyServeResults.cpp 
b/extensions/civetweb/tests/C2VerifyServeResults.cpp
index 9e3de470c..7c46e2c82 100644
--- a/extensions/civetweb/tests/C2VerifyServeResults.cpp
+++ b/extensions/civetweb/tests/C2VerifyServeResults.cpp
@@ -53,7 +53,7 @@ class VerifyC2Server : public HTTPIntegrationBase {
   }
 
   void runAssertions() override {
-    
REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(std::chrono::milliseconds(wait_time_),
+    REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(wait_time_,
         "Import offset 0",
         "Outputting success and response"));
   }
diff --git a/extensions/civetweb/tests/HttpPostIntegrationTest.cpp 
b/extensions/civetweb/tests/HttpPostIntegrationTest.cpp
index e61a4e5f9..359868532 100644
--- a/extensions/civetweb/tests/HttpPostIntegrationTest.cpp
+++ b/extensions/civetweb/tests/HttpPostIntegrationTest.cpp
@@ -66,7 +66,7 @@ class HttpTestHarness : public HTTPIntegrationBase {
   }
 
   void runAssertions() override {
-    
REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(std::chrono::milliseconds(wait_time_),
+    REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(wait_time_,
       "curl performed",
       "Size:1024 Offset:0"));
     
REQUIRE_FALSE(minifi::test::utils::verifyLogLinePresenceInPollTime(std::chrono::milliseconds(200),
 "Size:0 Offset:0"));
diff --git a/extensions/elasticsearch/tests/CMakeLists.txt 
b/extensions/elasticsearch/tests/CMakeLists.txt
index ea63fff49..1b91fbafe 100644
--- a/extensions/elasticsearch/tests/CMakeLists.txt
+++ b/extensions/elasticsearch/tests/CMakeLists.txt
@@ -32,6 +32,6 @@ FOREACH(testfile ${ELASTICSEARCH_TESTS})
     target_link_libraries(${testfilename} Catch2WithMain 
civetweb::civetweb-cpp minifi-elasticsearch minifi-standard-processors)
     MATH(EXPR ELASTICSEARCH_TEST_COUNT "${ELASTICSEARCH_TEST_COUNT}+1")
     add_test(NAME "${testfilename}" COMMAND "${testfilename}" 
WORKING_DIRECTORY ${TEST_DIR})
-    set_tests_properties("${testfilename}" PROPERTIES LABELS 
"elasticsearch;memchecked")
+    set_tests_properties("${testfilename}" PROPERTIES LABELS "elasticsearch")
 ENDFOREACH()
 message("-- Finished building ${ELASTICSEARCH_TEST_COUNT} Elasticsearch 
related test file(s)...")
diff --git a/extensions/execute-process/test/CMakeLists.txt 
b/extensions/execute-process/test/CMakeLists.txt
index 96f4a0e51..70efe71b1 100644
--- a/extensions/execute-process/test/CMakeLists.txt
+++ b/extensions/execute-process/test/CMakeLists.txt
@@ -27,7 +27,7 @@ target_include_directories(ExecuteProcessTests PRIVATE BEFORE 
"${CMAKE_SOURCE_DI
 target_compile_definitions(ExecuteProcessTests PRIVATE 
"MINIFI_EXTENSION_GROUP_NAME=org.apache.nifi.minifi.test")
 
 add_test(NAME ExecuteProcessTests COMMAND ExecuteProcessTests 
WORKING_DIRECTORY ${TEST_DIR})
-set_tests_properties(ExecuteProcessTests PROPERTIES LABELS 
"execute-process;memchecked")
+set_tests_properties(ExecuteProcessTests PROPERTIES LABELS "execute-process")
 
 createTests(ExecuteProcessTests)
 
diff --git a/extensions/grafana-loki/tests/CMakeLists.txt 
b/extensions/grafana-loki/tests/CMakeLists.txt
index ee9ee6246..d048b9bf2 100644
--- a/extensions/grafana-loki/tests/CMakeLists.txt
+++ b/extensions/grafana-loki/tests/CMakeLists.txt
@@ -41,6 +41,6 @@ FOREACH(testfile ${GRAFANA_LOKI_TESTS})
     target_link_libraries(${testfilename} Catch2WithMain 
civetweb::civetweb-cpp minifi-grafana-loki minifi-standard-processors)
     MATH(EXPR GRAFANA_LOKI_TEST_COUNT "${GRAFANA_LOKI_TEST_COUNT}+1")
     add_test(NAME "${testfilename}" COMMAND "${testfilename}" 
WORKING_DIRECTORY ${TEST_DIR})
-    set_tests_properties("${testfilename}" PROPERTIES LABELS 
"grafana-loki;memchecked")
+    set_tests_properties("${testfilename}" PROPERTIES LABELS "grafana-loki")
 ENDFOREACH()
 message("-- Finished building ${GRAFANA_LOKI_TEST_COUNT} Grafana Loki related 
test file(s)...")
diff --git a/extensions/kafka/tests/CMakeLists.txt 
b/extensions/kafka/tests/CMakeLists.txt
index 01cbd1a2f..42e634d1b 100644
--- a/extensions/kafka/tests/CMakeLists.txt
+++ b/extensions/kafka/tests/CMakeLists.txt
@@ -32,6 +32,6 @@ FOREACH (testfile ${KAFKA_INTEGRATION_TESTS})
     target_link_libraries(${testfilename} mock-minifi)
     MATH(EXPR EXTENSIONS_TEST_COUNT "${EXTENSIONS_TEST_COUNT}+1")
     add_test(NAME "${testfilename}" COMMAND "${testfilename}" 
WORKING_DIRECTORY ${TEST_DIR})
-    set_tests_properties("${testfilename}" PROPERTIES LABELS 
"librdkafka;memchecked")
+    set_tests_properties("${testfilename}" PROPERTIES LABELS "librdkafka")
 ENDFOREACH()
 message("-- Finished building ${KAFKA-EXTENSIONS_TEST_COUNT} Lib Kafka related 
test file(s)...")
diff --git a/extensions/kubernetes/tests/CMakeLists.txt 
b/extensions/kubernetes/tests/CMakeLists.txt
index 3a037ecc0..26127ae74 100644
--- a/extensions/kubernetes/tests/CMakeLists.txt
+++ b/extensions/kubernetes/tests/CMakeLists.txt
@@ -30,6 +30,6 @@ FOREACH(TEST_FILE ${KUBERNETES_UNIT_TESTS})
     createTests("${TEST_TARGET}")
     add_test(NAME ${TEST_TARGET} COMMAND "${TEST_TARGET}" WORKING_DIRECTORY 
"${TEST_DIR}")
     MATH(EXPR KUBERNETES_UNIT_TEST_COUNT "${KUBERNETES_UNIT_TEST_COUNT}+1")
-    set_tests_properties("${TEST_TARGET}" PROPERTIES LABELS 
"kubernetes;memchecked")
+    set_tests_properties("${TEST_TARGET}" PROPERTIES LABELS "kubernetes")
 ENDFOREACH()
 message("-- Finished building ${KUBERNETES_UNIT_TEST_COUNT} Kubernetes related 
test file(s)...")
diff --git a/extensions/llamacpp/tests/CMakeLists.txt 
b/extensions/llamacpp/tests/CMakeLists.txt
index a9f267ab0..56f65090c 100644
--- a/extensions/llamacpp/tests/CMakeLists.txt
+++ b/extensions/llamacpp/tests/CMakeLists.txt
@@ -35,6 +35,6 @@ FOREACH(testfile ${LLAMACPP_TESTS})
 
     MATH(EXPR EXTENSIONS_TEST_COUNT "${EXTENSIONS_TEST_COUNT}+1")
     add_test(NAME ${testfilename} COMMAND ${testfilename} WORKING_DIRECTORY 
${TEST_DIR})
-    set_tests_properties("${testfilename}" PROPERTIES LABELS 
"llamacpp;memchecked")
+    set_tests_properties("${testfilename}" PROPERTIES LABELS "llamacpp")
 ENDFOREACH()
 message("-- Finished building ${EXTENSIONS_TEST_COUNT} llama.cpp related test 
file(s)...")
diff --git a/extensions/lua/tests/CMakeLists.txt 
b/extensions/lua/tests/CMakeLists.txt
index e959b0ace..4fd50f9bc 100644
--- a/extensions/lua/tests/CMakeLists.txt
+++ b/extensions/lua/tests/CMakeLists.txt
@@ -31,7 +31,7 @@ FOREACH(testfile ${EXECUTESCRIPT_LUA_TESTS})
     createTests("${testfilename}")
     MATH(EXPR EXTENSIONS_TEST_COUNT "${EXTENSIONS_TEST_COUNT}+1")
     add_test(NAME "${testfilename}" COMMAND "${testfilename}" 
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
-    set_tests_properties("${testfilename}" PROPERTIES LABELS "lua;memchecked")
+    set_tests_properties("${testfilename}" PROPERTIES LABELS "lua")
 ENDFOREACH()
 
 copyTestResources(${CMAKE_CURRENT_SOURCE_DIR}/test_lua_scripts/ 
${CMAKE_BINARY_DIR}/bin/resources/test_lua_scripts/)
diff --git a/extensions/mqtt/tests/CMakeLists.txt 
b/extensions/mqtt/tests/CMakeLists.txt
index 9ffa1e8b8..32873027d 100644
--- a/extensions/mqtt/tests/CMakeLists.txt
+++ b/extensions/mqtt/tests/CMakeLists.txt
@@ -30,7 +30,7 @@ FOREACH(testfile ${MQTT_TESTS})
        target_link_libraries(${testfilename} minifi-mqtt-extensions)
        target_link_libraries(${testfilename} minifi-standard-processors)
        add_test(NAME "${testfilename}" COMMAND "${testfilename}" 
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
-       set_tests_properties("${testfilename}" PROPERTIES LABELS 
"mqtt;memchecked")
+       set_tests_properties("${testfilename}" PROPERTIES LABELS "mqtt")
 ENDFOREACH()
 
 list(LENGTH MQTT_TESTS TEST_COUNT)
diff --git a/extensions/opc/tests/CMakeLists.txt 
b/extensions/opc/tests/CMakeLists.txt
index 9d7354593..64b1bafa3 100644
--- a/extensions/opc/tests/CMakeLists.txt
+++ b/extensions/opc/tests/CMakeLists.txt
@@ -29,7 +29,7 @@ FOREACH(testfile ${OPC_UA_TESTS})
     target_link_libraries(${testfilename} minifi-opc-extensions)
     MATH(EXPR OPC_UA_TEST_COUNT "${OPC_UA_TEST_COUNT}+1")
     add_test(NAME "${testfilename}" COMMAND "${testfilename}")
-    set_tests_properties("${testfilename}" PROPERTIES LABELS "opc;memchecked")
+    set_tests_properties("${testfilename}" PROPERTIES LABELS "opc")
     target_compile_definitions(${testfilename} PRIVATE 
"MINIFI_EXTENSION_GROUP_NAME=org.apache.nifi.minifi.test")
 ENDFOREACH()
 message("-- Finished building ${OPC_UA_TEST_COUNT} OPC UA related test 
file(s)...")
diff --git a/extensions/pdh/tests/CMakeLists.txt 
b/extensions/pdh/tests/CMakeLists.txt
index c1459f767..4d92d1df3 100644
--- a/extensions/pdh/tests/CMakeLists.txt
+++ b/extensions/pdh/tests/CMakeLists.txt
@@ -32,6 +32,6 @@ FOREACH(testfile ${PDH_TESTS})
     target_link_libraries(${testfilename} minifi-standard-processors)
     MATH(EXPR PDH_TEST_COUNT "${PDH_TEST_COUNT}+1")
     add_test(NAME "${testfilename}" COMMAND "${testfilename}" 
WORKING_DIRECTORY ${TEST_DIR})
-    set_tests_properties("${testfilename}" PROPERTIES LABELS "phd;memchecked")
+    set_tests_properties("${testfilename}" PROPERTIES LABELS "pdh")
 ENDFOREACH()
 message("-- Finished building ${PDH_TEST_COUNT} PDH related test file(s)...")
diff --git a/extensions/procfs/tests/CMakeLists.txt 
b/extensions/procfs/tests/CMakeLists.txt
index 4c3238cb3..b61fedca0 100644
--- a/extensions/procfs/tests/CMakeLists.txt
+++ b/extensions/procfs/tests/CMakeLists.txt
@@ -33,7 +33,7 @@ FOREACH(testfile ${PROCFS_TESTS})
     target_link_libraries(${testfilename} minifi-standard-processors)
     MATH(EXPR PROCFS_TEST_COUNT "${PROCFS_TEST_COUNT}+1")
     add_test(NAME "${testfilename}" COMMAND "${testfilename}")
-    set_tests_properties("${testfilename}" PROPERTIES LABELS 
"procfs;memchecked")
+    set_tests_properties("${testfilename}" PROPERTIES LABELS "procfs")
 ENDFOREACH()
 file(COPY "${CMAKE_SOURCE_DIR}/extensions/procfs/tests/mockprocfs_t0" 
DESTINATION "${CMAKE_BINARY_DIR}/bin/procfs-test/")
 file(COPY "${CMAKE_SOURCE_DIR}/extensions/procfs/tests/mockprocfs_t1" 
DESTINATION "${CMAKE_BINARY_DIR}/bin/procfs-test/")
diff --git a/extensions/prometheus/tests/CMakeLists.txt 
b/extensions/prometheus/tests/CMakeLists.txt
index aefc1f02c..aecf73427 100644
--- a/extensions/prometheus/tests/CMakeLists.txt
+++ b/extensions/prometheus/tests/CMakeLists.txt
@@ -30,6 +30,6 @@ FOREACH(testfile ${PROMETHEUS_TESTS})
     target_link_libraries(${testfilename} minifi-prometheus)
     MATH(EXPR PROMETHEUS_TEST_COUNT "${PROMETHEUS_TEST_COUNT}+1")
     add_test(NAME "${testfilename}" COMMAND "${testfilename}")
-    set_tests_properties("${testfilename}" PROPERTIES LABELS 
"prometheus;memchecked")
+    set_tests_properties("${testfilename}" PROPERTIES LABELS "prometheus")
 ENDFOREACH()
 message("-- Finished building ${PROMETHEUS_TEST_COUNT} Prometheus related test 
file(s)...")
diff --git a/extensions/rocksdb-repos/tests/RepoTests.cpp 
b/extensions/rocksdb-repos/tests/RepoTests.cpp
index 4041f5771..79b887d18 100644
--- a/extensions/rocksdb-repos/tests/RepoTests.cpp
+++ b/extensions/rocksdb-repos/tests/RepoTests.cpp
@@ -363,6 +363,11 @@ TEST_CASE("Flush deleted flowfiles before shutdown", 
"[TestFFR7]") {
     explicit TestFlowFileRepository(const std::string& name)
       : FlowFileRepository(name, 
core::repository::FLOWFILE_REPOSITORY_DIRECTORY,
                            10min, 
core::repository::MAX_FLOWFILE_REPOSITORY_STORAGE_SIZE, 50ms) {}
+    TestFlowFileRepository(const TestFlowFileRepository&) = delete;
+    TestFlowFileRepository(TestFlowFileRepository&&) = delete;
+    TestFlowFileRepository& operator=(const TestFlowFileRepository&) = delete;
+    TestFlowFileRepository& operator=(TestFlowFileRepository&&) = delete;
+    ~TestFlowFileRepository() override { stop(); }
 
     void flush() override {
       FlowFileRepository::flush();
diff --git a/extensions/stable-api-testing/tests/CMakeLists.txt 
b/extensions/stable-api-testing/tests/CMakeLists.txt
index 18e9d759e..ce22f7b76 100644
--- a/extensions/stable-api-testing/tests/CMakeLists.txt
+++ b/extensions/stable-api-testing/tests/CMakeLists.txt
@@ -32,5 +32,5 @@ FOREACH (testfile ${SOURCES})
 
     MATH(EXPR EXTENSIONS_TEST_COUNT "${EXTENSIONS_TEST_COUNT}+1")
     add_test(NAME ${testfilename} COMMAND ${testfilename} WORKING_DIRECTORY 
${TEST_DIR})
-    set_tests_properties("${testfilename}" PROPERTIES LABELS "memchecked")
+    set_tests_properties("${testfilename}" PROPERTIES LABELS 
"stable-api-testing")
 ENDFOREACH ()
diff --git 
a/extensions/standard-processors/tests/integration/ProcessGroupTest.cpp 
b/extensions/standard-processors/tests/integration/ProcessGroupTest.cpp
index e642399b9..529956177 100644
--- a/extensions/standard-processors/tests/integration/ProcessGroupTest.cpp
+++ b/extensions/standard-processors/tests/integration/ProcessGroupTest.cpp
@@ -44,8 +44,7 @@ class ProcessGroupTestHarness : public IntegrationBase {
 
   void runAssertions() override {
     using 
org::apache::nifi::minifi::test::utils::verifyLogLinePresenceInPollTime;
-    
REQUIRE(verifyLogLinePresenceInPollTime(std::chrono::milliseconds(wait_time_),
-        "key:test_attribute value:success"));
+    REQUIRE(verifyLogLinePresenceInPollTime(wait_time_, "key:test_attribute 
value:success"));
   }
 };
 
diff --git 
a/extensions/standard-processors/tests/unit/AttributeRollingWindowTests.cpp 
b/extensions/standard-processors/tests/unit/AttributeRollingWindowTests.cpp
index 13fb7f93b..c7a682036 100644
--- a/extensions/standard-processors/tests/unit/AttributeRollingWindowTests.cpp
+++ b/extensions/standard-processors/tests/unit/AttributeRollingWindowTests.cpp
@@ -15,8 +15,11 @@
  * limitations under the License.
  */
 
+#include <algorithm>
 #include <memory>
+#include <ranges>
 #include <string_view>
+#include "fmt/ranges.h"
 #include "unit/Catch.h"
 #include "AttributeRollingWindow.h"
 #include "unit/SingleProcessorTestController.h"
@@ -29,10 +32,12 @@ using AttributeRollingWindow = 
processors::AttributeRollingWindow;
 bool checkAttributes(const std::map<std::string, std::string>& expected, const 
std::map<std::string, std::string>& actual) {
   // expected may be incomplete, but if something is specified in expected, 
they also need to be in the actual
   // set of attributes
-  return std::all_of(std::begin(expected), std::end(expected), [&actual](const 
auto& kvpair) {
+  return std::ranges::all_of(expected, [&actual](const auto& kvpair) {
     const auto& key = kvpair.first;
-    const auto& value = kvpair.second;
-    return actual.contains(key) && actual.at(key) == value;
+    if (!actual.contains(key)) { return false; }
+    double expected_value = std::stod(kvpair.second);
+    double actual_value = std::stod(actual.at(key));
+    return std::abs(expected_value - actual_value) < 0.000001;
   });
 }
 
@@ -48,6 +53,7 @@ TEST_CASE("AttributeRollingWindow properly forwards 
properties to RollingWindow
     const auto out_flow_files = out.at(rel("success"));
     REQUIRE(out_flow_files.size() == 1);
     const auto out_attrs = out_flow_files[0]->getAttributes();
+    INFO(fmt::format("expected: {}\nactual: {}", expected_out_attributes, 
out_attrs));
     REQUIRE(checkAttributes(expected_out_attributes, out_attrs));
   };
   trigger_with_value_and_check_attributes("1", {
diff --git a/extensions/standard-processors/tests/unit/GetTCPTests.cpp 
b/extensions/standard-processors/tests/unit/GetTCPTests.cpp
index a071c9148..bf4080e09 100644
--- a/extensions/standard-processors/tests/unit/GetTCPTests.cpp
+++ b/extensions/standard-processors/tests/unit/GetTCPTests.cpp
@@ -25,6 +25,7 @@
 #include "controllers/SSLContextService.h"
 #include "range/v3/algorithm/contains.hpp"
 #include "minifi-cpp/utils/gsl.h"
+#include "fmt/chrono.h"
 
 using GetTCP = org::apache::nifi::minifi::processors::GetTCP;
 
@@ -155,9 +156,9 @@ TEST_CASE("GetTCP test with delimiter", "[GetTCP]") {
   SingleProcessorTestController 
controller{minifi::test::utils::make_processor<GetTCP>("GetTCP")};
   const auto get_tcp = controller.getProcessor();
   LogTestController::getInstance().setTrace<GetTCP>();
+  REQUIRE(get_tcp->setProperty(GetTCP::Timeout.name, fmt::format("{}", 
minifi::test::utils::timeout(1s))));
   REQUIRE(get_tcp->setProperty(GetTCP::MaxBatchSize.name, "2"));
 
-
   TcpTestServer tcp_test_server;
 
   SECTION("No SSL") {}
@@ -186,6 +187,7 @@ TEST_CASE("GetTCP test with too large message", "[GetTCP]") 
{
   SingleProcessorTestController 
controller{minifi::test::utils::make_processor<GetTCP>("GetTCP")};
   const auto get_tcp = controller.getProcessor();
   LogTestController::getInstance().setTrace<GetTCP>();
+  REQUIRE(get_tcp->setProperty(GetTCP::Timeout.name, fmt::format("{}", 
minifi::test::utils::timeout(1s))));
   REQUIRE(get_tcp->setProperty(GetTCP::MaxBatchSize.name, "2"));
   REQUIRE(get_tcp->setProperty(GetTCP::MaxMessageSize.name, "10"));
   REQUIRE(get_tcp->setProperty(GetTCP::MessageDelimiter.name, "\r"));
@@ -227,6 +229,7 @@ TEST_CASE("GetTCP test multiple endpoints", "[GetTCP]") {
   SingleProcessorTestController 
controller{minifi::test::utils::make_processor<GetTCP>("GetTCP")};
   const auto get_tcp = controller.getProcessor();
   LogTestController::getInstance().setTrace<GetTCP>();
+  REQUIRE(get_tcp->setProperty(GetTCP::Timeout.name, fmt::format("{}", 
minifi::test::utils::timeout(1s))));
   REQUIRE(get_tcp->setProperty(GetTCP::MaxBatchSize.name, "2"));
 
   TcpTestServer server_1;
@@ -271,6 +274,7 @@ TEST_CASE("GetTCP max queue and max batch size test", 
"[GetTCP]") {
   SingleProcessorTestController 
controller{minifi::test::utils::make_processor<GetTCP>("GetTCP")};
   const auto get_tcp = controller.getProcessor();
   LogTestController::getInstance().setTrace<GetTCP>();
+  REQUIRE(get_tcp->setProperty(GetTCP::Timeout.name, fmt::format("{}", 
minifi::test::utils::timeout(1s))));
   REQUIRE(get_tcp->setProperty(GetTCP::MaxBatchSize.name, "10"));
   REQUIRE(get_tcp->setProperty(GetTCP::MaxQueueSize.name, "50"));
 
diff --git a/extensions/standard-processors/tests/unit/ProcessorTests.cpp 
b/extensions/standard-processors/tests/unit/ProcessorTests.cpp
index 4ce6c206e..4589f1ac9 100644
--- a/extensions/standard-processors/tests/unit/ProcessorTests.cpp
+++ b/extensions/standard-processors/tests/unit/ProcessorTests.cpp
@@ -809,6 +809,6 @@ TEST_CASE("Test getProcessorType", "[getProcessorType]") {
 
 TEST_CASE("IsYield and getYieldTime is consistent") {
   auto processor = 
minifi::test::utils::make_processor<TestProcessorNoContent>("test_processor");
-  processor->yield(1ms);
+  processor->yield(minifi::test::utils::timeout(1ms));
   REQUIRE(processor->isYield() == (processor->getYieldTime() != 0ms));
 }
diff --git a/extensions/standard-processors/tests/unit/PutTCPTests.cpp 
b/extensions/standard-processors/tests/unit/PutTCPTests.cpp
index 62b8c4e51..fdbf50bea 100644
--- a/extensions/standard-processors/tests/unit/PutTCPTests.cpp
+++ b/extensions/standard-processors/tests/unit/PutTCPTests.cpp
@@ -29,6 +29,8 @@
 #include "utils/net/AsioCoro.h"
 #include "utils/expected.h"
 #include "unit/TestUtils.h"
+#include "asio/experimental/awaitable_operators.hpp"
+#include "fmt/chrono.h"
 
 using namespace std::literals::chrono_literals;
 using 
org::apache::nifi::minifi::test::utils::verifyLogLineVariantPresenceInPollTime;
@@ -105,7 +107,7 @@ class PutTCPTestFixture {
     LogTestController::getInstance().setInfo<core::ProcessSession>();
     LogTestController::getInstance().setTrace<utils::net::Server>();
     REQUIRE(put_tcp_->setProperty(PutTCP::Hostname.name, 
"${literal('localhost')}"));
-    REQUIRE(put_tcp_->setProperty(PutTCP::Timeout.name, "200 ms"));
+    REQUIRE(put_tcp_->setProperty(PutTCP::Timeout.name, fmt::format("{}", 
minifi::test::utils::timeout(200ms))));
     REQUIRE(put_tcp_->setProperty(PutTCP::OutgoingMessageDelimiter.name, 
"\n"));
   }
 
@@ -143,7 +145,7 @@ class PutTCPTestFixture {
         cancellable_tcp_server->cancelEverything();
       }
     }
-    std::this_thread::sleep_for(200ms);
+    std::this_thread::sleep_for(minifi::test::utils::timeout(200ms));
   }
 
   auto trigger(std::string_view message, std::unordered_map<std::string, 
std::string> input_flow_file_attributes = {}) {
@@ -155,7 +157,7 @@ class PutTCPTestFixture {
   }
 
   std::optional<utils::net::Message> 
tryDequeueReceivedMessage(std::optional<uint16_t> port = std::nullopt) {
-    auto timeout = 200ms;
+    auto timeout = minifi::test::utils::timeout(200ms);
     auto interval = 10ms;
 
     auto start_time = std::chrono::system_clock::now();
diff --git a/libminifi/test/flow-tests/FlowControllerTests.cpp 
b/libminifi/test/flow-tests/FlowControllerTests.cpp
index 9e0bb90d8..2c0a7d495 100644
--- a/libminifi/test/flow-tests/FlowControllerTests.cpp
+++ b/libminifi/test/flow-tests/FlowControllerTests.cpp
@@ -89,17 +89,6 @@ Controller Services:
           - value: flowcontrollertests_state.txt
 )";
 
-template<typename Fn>
-bool verifyWithBusyWait(std::chrono::milliseconds timeout, const Fn& fn) {
-  auto start = std::chrono::steady_clock::now();
-  while (std::chrono::steady_clock::now() - start < timeout) {
-    if (fn()) {
-      return true;
-    }
-  }
-  return false;
-}
-
 TEST_CASE("Flow shutdown drains connections", "[TestFlow1]") {
   TestControllerWithFlow testController(yamlConfig);
   auto controller = testController.controller_;
@@ -156,7 +145,7 @@ TEST_CASE("Flow shutdown waits for a while", "[TestFlow2]") 
{
 
   // wait for the source processor to enqueue its flowFiles
   auto flowFilesEnqueued = [&] { return root->getTotalFlowFileCount() >= 3; };
-  REQUIRE(verifyWithBusyWait(std::chrono::milliseconds{500}, 
flowFilesEnqueued));
+  REQUIRE(minifi::test::utils::verifyEventHappenedInPollTime(500ms, 
flowFilesEnqueued));
 
   REQUIRE(sourceProc.get().trigger_count.load() >= 1);
 
@@ -193,7 +182,7 @@ TEST_CASE("Flow stopped after grace period", "[TestFlow3]") 
{
 
   // wait for the source processor to enqueue its flowFiles
   auto flowFilesEnqueued = [&] { return root->getTotalFlowFileCount() >= 3; };
-  REQUIRE(verifyWithBusyWait(std::chrono::milliseconds{500}, 
flowFilesEnqueued));
+  REQUIRE(minifi::test::utils::verifyEventHappenedInPollTime(500ms, 
flowFilesEnqueued));
 
   REQUIRE(sourceProc.get().trigger_count.load() >= 1);
 
@@ -232,7 +221,7 @@ TEST_CASE("Extend the waiting period during shutdown", 
"[TestFlow4]") {
 
   // wait for the source processor to enqueue its flowFiles
   auto flowFilesEnqueued = [&] { return root->getTotalFlowFileCount() >= 3; };
-  REQUIRE(verifyWithBusyWait(std::chrono::milliseconds{500}, 
flowFilesEnqueued));
+  REQUIRE(minifi::test::utils::verifyEventHappenedInPollTime(500ms, 
flowFilesEnqueued));
 
   REQUIRE(sourceProc.get().trigger_count.load() >= 1);
 
diff --git a/libminifi/test/integration/CMakeLists.txt 
b/libminifi/test/integration/CMakeLists.txt
index 2e0ff7b7d..23ddf1836 100644
--- a/libminifi/test/integration/CMakeLists.txt
+++ b/libminifi/test/integration/CMakeLists.txt
@@ -33,6 +33,7 @@ FOREACH(testfile ${INTEGRATION_TESTS})
     target_compile_definitions(${testfilename} PRIVATE 
"MINIFI_EXTENSION_GROUP_NAME=org.apache.nifi.minifi.test")
     if(NOT ${testfilename} IN_LIST TESTS_WITH_LIBARCHIVE_DEPENDENCY)
         add_test(NAME "${testfilename}" COMMAND "${testfilename}")
+        set_tests_properties("${testfilename}" PROPERTIES LABELS "integration")
     endif()
     MATH(EXPR INT_TEST_COUNT "${INT_TEST_COUNT}+1")
 ENDFOREACH()
@@ -40,6 +41,7 @@ ENDFOREACH()
 FOREACH(testfilename ${TESTS_WITH_LIBARCHIVE_DEPENDENCY})
     if (ENABLE_LIBARCHIVE)
         add_test(NAME "${testfilename}" COMMAND "${testfilename}")
+        set_tests_properties("${testfilename}" PROPERTIES LABELS "integration")
     endif()
 ENDFOREACH()
 
diff --git a/libminifi/test/libtest/integration/HTTPIntegrationBase.h 
b/libminifi/test/libtest/integration/HTTPIntegrationBase.h
index f15e39205..461adcbe5 100644
--- a/libminifi/test/libtest/integration/HTTPIntegrationBase.h
+++ b/libminifi/test/libtest/integration/HTTPIntegrationBase.h
@@ -40,7 +40,7 @@ int ssl_enable(void* /*ssl_context*/, void* /*user_data*/);
 class HTTPIntegrationBase : public IntegrationBase {
  public:
   explicit HTTPIntegrationBase(const std::optional<std::filesystem::path>& 
test_file_location = {}, const std::optional<std::filesystem::path>& home_path 
= {},
-    std::chrono::milliseconds waitTime = 
std::chrono::milliseconds(DEFAULT_WAITTIME_MSECS))
+    std::chrono::milliseconds waitTime = DefaultWaitTime)
       : IntegrationBase(test_file_location, home_path, waitTime),
         server(nullptr) {
   }
@@ -103,7 +103,7 @@ class VerifyC2Describe : public VerifyC2Base {
   }
 
   void runAssertions() override {
-    
REQUIRE(utils::verifyEventHappenedInPollTime(std::chrono::milliseconds(wait_time_),
 [&] { return verified_.load(); }));
+    REQUIRE(utils::verifyEventHappenedInPollTime(wait_time_, [&] { return 
verified_.load(); }));
   }
 
  protected:
@@ -113,7 +113,7 @@ class VerifyC2Describe : public VerifyC2Base {
 class VerifyC2Update : public HTTPIntegrationBase {
  public:
   explicit VerifyC2Update(const std::filesystem::path& test_file_location, 
const std::optional<std::filesystem::path>& home_path = {},
-    std::chrono::milliseconds waitTime = 
std::chrono::milliseconds(DEFAULT_WAITTIME_MSECS))
+    std::chrono::milliseconds waitTime = DefaultWaitTime)
       : HTTPIntegrationBase(test_file_location, home_path, waitTime) {
   }
 
@@ -136,7 +136,7 @@ class VerifyC2Update : public HTTPIntegrationBase {
   }
 
   void runAssertions() override {
-    
REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(std::chrono::seconds(10),
 "Starting to reload Flow Controller with flow control name MiNiFi Flow, 
version"));
+    REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(10s, 
"Starting to reload Flow Controller with flow control name MiNiFi Flow, 
version"));
   }
 };
 
@@ -167,7 +167,7 @@ class VerifyFlowFetched : public HTTPIntegrationBase {
   }
 
   void runAssertions() override {
-    
REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(std::chrono::seconds(10),
 "Successfully fetched valid flow configuration"));
+    REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(10s, 
"Successfully fetched valid flow configuration"));
   }
 };
 
@@ -184,7 +184,7 @@ class VerifyC2FailedUpdate : public VerifyC2Update {
   }
 
   void runAssertions() override {
-    
REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(std::chrono::seconds(10),
 "Invalid configuration payload", "update failed"));
+    REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(10s, "Invalid 
configuration payload", "update failed"));
   }
 
   void cleanup() override {
diff --git a/libminifi/test/libtest/integration/IntegrationBase.cpp 
b/libminifi/test/libtest/integration/IntegrationBase.cpp
index 8a767546a..753acda82 100644
--- a/libminifi/test/libtest/integration/IntegrationBase.cpp
+++ b/libminifi/test/libtest/integration/IntegrationBase.cpp
@@ -158,6 +158,7 @@ void IntegrationBase::run() {
     flowController_->stop();
   }
 
+  flowController_.reset();  // stop the C2Agent before cleanup() resets the 
logger
   cleanup();
 }
 
diff --git a/libminifi/test/libtest/integration/IntegrationBase.h 
b/libminifi/test/libtest/integration/IntegrationBase.h
index 9f406d57a..1b68a2317 100644
--- a/libminifi/test/libtest/integration/IntegrationBase.h
+++ b/libminifi/test/libtest/integration/IntegrationBase.h
@@ -17,8 +17,6 @@
  */
 #pragma once
 
-#define DEFAULT_WAITTIME_MSECS 10000
-
 #include <memory>
 #include <optional>
 #include <string>
@@ -32,6 +30,7 @@
 #include "utils/file/FileUtils.h"
 #include "core/BulletinStore.h"
 #include "unit/TestBase.h"
+#include "unit/TestUtils.h"
 
 namespace minifi = org::apache::nifi::minifi;
 namespace core = minifi::core;
@@ -46,8 +45,10 @@ struct FlowConfigPath {
 
 class IntegrationBase {
  public:
+  static constexpr auto DefaultWaitTime = 10s;
+
   explicit IntegrationBase(const std::optional<std::filesystem::path>& 
test_file_location = {}, const std::optional<std::filesystem::path>& home_path 
= {},
-      std::chrono::milliseconds waitTime = 
std::chrono::milliseconds(DEFAULT_WAITTIME_MSECS));
+      std::chrono::milliseconds waitTime = DefaultWaitTime);
   IntegrationBase(const IntegrationBase&) = delete;
   IntegrationBase(IntegrationBase&& other) noexcept
       : logger_properties_{std::move(other.logger_properties_)},
diff --git a/libminifi/test/libtest/unit/SingleProcessorTestController.cpp 
b/libminifi/test/libtest/unit/SingleProcessorTestController.cpp
index fe2a04fc7..c7624e667 100644
--- a/libminifi/test/libtest/unit/SingleProcessorTestController.cpp
+++ b/libminifi/test/libtest/unit/SingleProcessorTestController.cpp
@@ -22,6 +22,7 @@
 #include "FlowFileRecord.h"
 #include "range/v3/algorithm/all_of.hpp"
 #include "Connection.h"
+#include "TestUtils.h"
 
 namespace org::apache::nifi::minifi::test {
 
@@ -63,7 +64,7 @@ bool SingleProcessorTestController::triggerUntil(const 
std::unordered_map<core::
                   const std::chrono::milliseconds max_duration,
                   const std::chrono::milliseconds wait_time) {
   auto start_time = std::chrono::steady_clock::now();
-  while (std::chrono::steady_clock::now() < start_time + max_duration) {
+  while (std::chrono::steady_clock::now() < start_time + 
utils::timeout(max_duration)) {
     for (auto& [relationship, flow_files] : trigger()) {
       result[relationship].insert(result[relationship].end(), 
flow_files.begin(), flow_files.end());
     }
diff --git a/libminifi/test/libtest/unit/TestUtils.cpp 
b/libminifi/test/libtest/unit/TestUtils.cpp
index d417089c0..0e1a283f8 100644
--- a/libminifi/test/libtest/unit/TestUtils.cpp
+++ b/libminifi/test/libtest/unit/TestUtils.cpp
@@ -25,6 +25,7 @@
 #endif
 
 #include "minifi-cpp/utils/gsl.h"
+#include "utils/Environment.h"
 
 #ifdef WIN32
 namespace {
@@ -63,6 +64,14 @@ void setAclOnFileOrDirectory(std::string file_name, DWORD 
perms, ACCESS_MODE per
 
 namespace org::apache::nifi::minifi::test::utils {
 
+bool runningUnderValgrind() {
+  static const bool result = [] {
+    const auto ld_preload = 
minifi::utils::Environment::getEnvironmentVariable("LD_PRELOAD");
+    return ld_preload && (ld_preload->contains("vgpreload") || 
ld_preload->contains("valgrind"));
+  }();
+  return result;
+}
+
 std::filesystem::path putFileToDir(const std::filesystem::path& dir_path, 
const std::filesystem::path& file_name, const std::string& content) {
   auto file_path = dir_path/file_name;
   std::ofstream out_file(file_path, std::ios::binary | std::ios::out);
@@ -162,7 +171,7 @@ bool countLogOccurrencesUntil(const std::string& pattern,
                               const std::chrono::milliseconds max_duration,
                               const std::chrono::milliseconds wait_time) {
   auto start_time = std::chrono::steady_clock::now();
-  while (std::chrono::steady_clock::now() < start_time + max_duration) {
+  while (std::chrono::steady_clock::now() < start_time + 
timeout(max_duration)) {
     if (LogTestController::getInstance().countOccurrences(pattern) == 
occurrences)
       return true;
     std::this_thread::sleep_for(wait_time);
diff --git a/libminifi/test/libtest/unit/TestUtils.h 
b/libminifi/test/libtest/unit/TestUtils.h
index 654fdf4fc..bbbe62f91 100644
--- a/libminifi/test/libtest/unit/TestUtils.h
+++ b/libminifi/test/libtest/unit/TestUtils.h
@@ -95,12 +95,21 @@ class ManualClock : public 
minifi::utils::timeutils::SteadyClock {
   std::chrono::milliseconds time_{0};
 };
 
+inline constexpr int VALGRIND_TIMEOUT_MULTIPLIER = 50;
+
+bool runningUnderValgrind();
+
+template <class Rep, class Period>
+std::chrono::duration<Rep, Period> timeout(std::chrono::duration<Rep, Period> 
duration) {
+  return runningUnderValgrind() ? duration * VALGRIND_TIMEOUT_MULTIPLIER : 
duration;
+}
+
 template <class Rep, class Period, typename Fun>
 bool verifyEventHappenedInPollTime(
     const std::chrono::duration<Rep, Period>& wait_duration,
     Fun&& check,
-    std::chrono::microseconds check_interval = std::chrono::milliseconds(100)) 
{
-  std::chrono::steady_clock::time_point wait_end = 
std::chrono::steady_clock::now() + wait_duration;
+    std::chrono::microseconds check_interval = 100ms) {
+  std::chrono::steady_clock::time_point wait_end = 
std::chrono::steady_clock::now() + timeout(wait_duration);
   do {
     if (std::forward<Fun>(check)()) {
       return true;
diff --git a/libminifi/test/unit/FlowFileQueueTests.cpp 
b/libminifi/test/unit/FlowFileQueueTests.cpp
index 73d1afe17..999cddbfa 100644
--- a/libminifi/test/unit/FlowFileQueueTests.cpp
+++ b/libminifi/test/unit/FlowFileQueueTests.cpp
@@ -49,7 +49,7 @@ TEST_CASE("A flow file can be moved into the FlowFileQueue", 
"[FlowFileQueue][po
   utils::FlowFileQueue queue;
 
   auto penalized_flow_file = std::make_shared<core::FlowFileImpl>();
-  penalized_flow_file->penalize(std::chrono::milliseconds{100});
+  penalized_flow_file->penalize(minifi::test::utils::timeout(100ms));
   queue.push(std::move(penalized_flow_file));
 
   queue.push(std::make_shared<core::FlowFileImpl>());
@@ -81,7 +81,7 @@ TEST_CASE("Cannot add flow files in the past preempting 
others", "[FlowFileQueue
   const auto flow_file_1 = std::make_shared<core::FlowFileImpl>();
   queue.push(flow_file_1);
   const auto flow_file_2 = std::make_shared<core::FlowFileImpl>();
-  flow_file_2->penalize(std::chrono::seconds{-10});
+  flow_file_2->penalize(-10s);
   queue.push(flow_file_2);
 
   REQUIRE(queue.isWorkAvailable());
@@ -107,33 +107,33 @@ class PenaltyHasExpired {
 TEST_CASE("Penalized flow files are popped from the FlowFileQueue in the order 
their penalties expire", "[FlowFileQueue][pop]") {
   utils::FlowFileQueue queue;
   const auto flow_file_1 = std::make_shared<core::FlowFileImpl>();
-  flow_file_1->penalize(std::chrono::milliseconds{70});
+  flow_file_1->penalize(minifi::test::utils::timeout(70ms));
   queue.push(flow_file_1);
   const auto flow_file_2 = std::make_shared<core::FlowFileImpl>();
-  flow_file_2->penalize(std::chrono::milliseconds{50});
+  flow_file_2->penalize(minifi::test::utils::timeout(50ms));
   queue.push(flow_file_2);
   const auto flow_file_3 = std::make_shared<core::FlowFileImpl>();
-  flow_file_3->penalize(std::chrono::milliseconds{80});
+  flow_file_3->penalize(minifi::test::utils::timeout(80ms));
   queue.push(flow_file_3);
   const auto flow_file_4 = std::make_shared<core::FlowFileImpl>();
-  flow_file_4->penalize(std::chrono::milliseconds{60});
+  flow_file_4->penalize(minifi::test::utils::timeout(60ms));
   queue.push(flow_file_4);
 
   REQUIRE_FALSE(queue.isWorkAvailable());
 
-  
REQUIRE(minifi::test::utils::verifyEventHappenedInPollTime(std::chrono::seconds{1},
 PenaltyHasExpired{flow_file_2}, std::chrono::milliseconds{10}));
+  REQUIRE(minifi::test::utils::verifyEventHappenedInPollTime(1s, 
PenaltyHasExpired{flow_file_2}, minifi::test::utils::timeout(10ms)));
   REQUIRE(queue.isWorkAvailable());
   REQUIRE(queue.pop() == flow_file_2);
 
-  
REQUIRE(minifi::test::utils::verifyEventHappenedInPollTime(std::chrono::seconds{1},
 PenaltyHasExpired{flow_file_4}, std::chrono::milliseconds{10}));
+  REQUIRE(minifi::test::utils::verifyEventHappenedInPollTime(1s, 
PenaltyHasExpired{flow_file_4}, minifi::test::utils::timeout(10ms)));
   REQUIRE(queue.isWorkAvailable());
   REQUIRE(queue.pop() == flow_file_4);
 
-  
REQUIRE(minifi::test::utils::verifyEventHappenedInPollTime(std::chrono::seconds{1},
 PenaltyHasExpired{flow_file_1}, std::chrono::milliseconds{10}));
+  REQUIRE(minifi::test::utils::verifyEventHappenedInPollTime(1s, 
PenaltyHasExpired{flow_file_1}, minifi::test::utils::timeout(10ms)));
   REQUIRE(queue.isWorkAvailable());
   REQUIRE(queue.pop() == flow_file_1);
 
-  
REQUIRE(minifi::test::utils::verifyEventHappenedInPollTime(std::chrono::seconds{1},
 PenaltyHasExpired{flow_file_3}, std::chrono::milliseconds{10}));
+  REQUIRE(minifi::test::utils::verifyEventHappenedInPollTime(1s, 
PenaltyHasExpired{flow_file_3}, minifi::test::utils::timeout(10ms)));
   REQUIRE(queue.isWorkAvailable());
   REQUIRE(queue.pop() == flow_file_3);
 
@@ -143,7 +143,7 @@ TEST_CASE("Penalized flow files are popped from the 
FlowFileQueue in the order t
 TEST_CASE("If a penalized then a non-penalized flow file is added to the 
FlowFileQueue, pop() returns the correct one", "[FlowFileQueue][pop]") {
   utils::FlowFileQueue queue;
   const auto penalized_flow_file = std::make_shared<core::FlowFileImpl>();
-  penalized_flow_file->penalize(std::chrono::milliseconds{10});
+  penalized_flow_file->penalize(minifi::test::utils::timeout(10ms));
   queue.push(penalized_flow_file);
   const auto flow_file = std::make_shared<core::FlowFileImpl>();
   queue.push(flow_file);
@@ -155,7 +155,7 @@ TEST_CASE("If a penalized then a non-penalized flow file is 
added to the FlowFil
   }
 
   SECTION("Wait until the penalty expires, then pop") {
-    
REQUIRE(minifi::test::utils::verifyEventHappenedInPollTime(std::chrono::seconds{1},
 PenaltyHasExpired{penalized_flow_file}, std::chrono::milliseconds{10}));
+    REQUIRE(minifi::test::utils::verifyEventHappenedInPollTime(1s, 
PenaltyHasExpired{penalized_flow_file}, minifi::test::utils::timeout(10ms)));
 
     REQUIRE(queue.isWorkAvailable());
     REQUIRE(queue.pop() == flow_file);
@@ -168,7 +168,7 @@ TEST_CASE("If a penalized then a non-penalized flow file is 
added to the FlowFil
 TEST_CASE("Force pop on FlowFileQueue returns the flow files, whether 
penalized or not", "[FlowFileQueue][forcePop]") {
   utils::FlowFileQueue queue;
   const auto penalized_flow_file = std::make_shared<core::FlowFileImpl>();
-  penalized_flow_file->penalize(std::chrono::milliseconds{10});
+  penalized_flow_file->penalize(minifi::test::utils::timeout(10ms));
   queue.push(penalized_flow_file);
   const auto flow_file = std::make_shared<core::FlowFileImpl>();
   queue.push(flow_file);
diff --git a/libminifi/test/unit/MemoryUsageTest.cpp 
b/libminifi/test/unit/MemoryUsageTest.cpp
index b818e90fa..f8bf834a7 100644
--- a/libminifi/test/unit/MemoryUsageTest.cpp
+++ b/libminifi/test/unit/MemoryUsageTest.cpp
@@ -26,9 +26,14 @@
 #include "minifi-cpp/utils/gsl.h"
 #include "utils/OsUtils.h"
 #include "unit/TestBase.h"
+#include "unit/TestUtils.h"
 #include "unit/Catch.h"
 
 TEST_CASE("Test Physical memory usage", "[testphysicalmemoryusage]") {
+  if (minifi::test::utils::runningUnderValgrind()) {
+    SKIP("Memory usage is much higher when running under Valgrind");
+  }
+
   constexpr bool cout_enabled = true;
 
   std::vector<uint8_t> large_vector(30'000'000);
diff --git a/libminifi/test/unit/MinifiConcurrentQueueTests.cpp 
b/libminifi/test/unit/MinifiConcurrentQueueTests.cpp
index dece31b50..469641b4e 100644
--- a/libminifi/test/unit/MinifiConcurrentQueueTests.cpp
+++ b/libminifi/test/unit/MinifiConcurrentQueueTests.cpp
@@ -136,7 +136,7 @@ namespace MinifiConcurrentQueueTestProducersConsumers {
       constexpr std::size_t max_read_attempts = 300;
       for (std::size_t attempt_num = 0; results.size() < 3 && attempt_num < 
max_read_attempts; ++attempt_num) {
         std::string s;
-        if (queue.dequeueWaitFor(s, std::chrono::milliseconds(1))) {
+        if (queue.dequeueWaitFor(s, minifi::test::utils::timeout(1ms))) {
           results.push_back(s);
         }
       }
@@ -240,11 +240,14 @@ TEST_CASE("TestConcurrentQueue: test simple producer and 
consumer", "[ProducerCo
         producer = 
MinifiConcurrentQueueTestProducersConsumers::getSimpleProducerThread(queue);
         consumer = 
MinifiConcurrentQueueTestProducersConsumers::getSimpleConsumeConsumerThread(queue,
 results);
     }
-    /* In this testcase the consumer thread puts back all items to the queue 
to consume again
-    * Even in this case the ones inserted later by the producer should be 
consumed */
-    SECTION("with readd") {
-      producer = 
MinifiConcurrentQueueTestProducersConsumers::getSimpleProducerThread(queue);
-      consumer = 
MinifiConcurrentQueueTestProducersConsumers::getSpinningReaddingDequeueConsumerThread(queue,
 results);
+    // In this testcase the consumer thread puts back all items to the queue 
to consume again
+    // Even in this case the ones inserted later by the producer should be 
consumed
+    // The spinning-readding consumer doesn't work well under Valgrind, so we 
skip it.
+    if (!minifi::test::utils::runningUnderValgrind()) {
+      SECTION("with readd") {
+        producer = 
MinifiConcurrentQueueTestProducersConsumers::getSimpleProducerThread(queue);
+        consumer = 
MinifiConcurrentQueueTestProducersConsumers::getSpinningReaddingDequeueConsumerThread(queue,
 results);
+      }
     }
     producer.join();
     consumer.join();
@@ -302,6 +305,10 @@ TEST_CASE("TestConcurrentQueue: test untimed waiting 
consumers", "[ProducerConsu
 }
 
 TEST_CASE("TestConcurrentQueue: test the readding dequeue consumer", 
"[ProducerConsumer]") {
+  if (minifi::test::utils::runningUnderValgrind()) {
+    SKIP("The spinning-readding consumer doesn't work well under Valgrind's 
serialized scheduler");
+  }
+
   utils::ConditionConcurrentQueue<std::string> queue(true);
   std::vector<std::string> results;
 
@@ -342,6 +349,10 @@ TEST_CASE("TestConcurrentQueue: test waiting consumers 
with blocked producer", "
 }
 
 TEST_CASE("TestConcurrentQueues::highLoad", "[TestConcurrentQueuesHighLoad]") {
+  if (minifi::test::utils::runningUnderValgrind()) {
+    SKIP("High-load busy-spin test is too slow under Valgrind's serialized 
scheduler");
+  }
+
   std::random_device dev;
   std::mt19937 rng(dev());
   std::uniform_int_distribution<std::mt19937::result_type> dist(1, 
std::numeric_limits<int>::max());

Reply via email to