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

bcall pushed a commit to branch unit-test-coverage
in repository https://gitbox.apache.org/repos/asf/trafficserver.git

commit d43e14376ca5f0b1f1b1e71605c5a712a07eccdd
Author: Bryan Call <[email protected]>
AuthorDate: Tue Jan 20 19:24:52 2026 -0800

    WIP: Unit test coverage infrastructure
    
    Phase 1 progress:
    - Add dev-coverage CMake preset for gcov instrumentation
    - Add tools/coverage-report.sh for generating coverage reports
    - Add src/test_utils/ with MockIOBuffer and TestEventProcessor
    
    Part of unit test coverage improvement plan.
---
 CMakeLists.txt                       |   3 +
 CMakePresets.json                    |  17 ++++
 src/test_utils/CMakeLists.txt        |  39 ++++++++
 src/test_utils/MockIOBuffer.cc       | 140 ++++++++++++++++++++++++++++
 src/test_utils/MockIOBuffer.h        | 146 ++++++++++++++++++++++++++++++
 src/test_utils/TestEventProcessor.cc |  73 +++++++++++++++
 src/test_utils/TestEventProcessor.h  | 115 +++++++++++++++++++++++
 tools/coverage-report.sh             | 171 +++++++++++++++++++++++++++++++++++
 8 files changed, 704 insertions(+)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7df4f8b63c..817a0cc5b8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -741,6 +741,9 @@ add_subdirectory(src/tsutil)
 add_subdirectory(src/tscore)
 add_subdirectory(src/records)
 add_subdirectory(src/iocore)
+if(BUILD_TESTING)
+  add_subdirectory(src/test_utils)
+endif()
 add_subdirectory(src/proxy)
 add_subdirectory(src/shared)
 add_subdirectory(src/mgmt/config)
diff --git a/CMakePresets.json b/CMakePresets.json
index 3fe7e010b8..996cc20af8 100644
--- a/CMakePresets.json
+++ b/CMakePresets.json
@@ -116,6 +116,23 @@
       "description": "Development Presets with ASAN sanitizer",
       "inherits": ["dev", "asan"]
     },
+    {
+      "name": "coverage",
+      "hidden": true,
+      "cacheVariables": {
+        "CMAKE_CXX_FLAGS": "-fprofile-arcs -ftest-coverage -g -O0",
+        "CMAKE_C_FLAGS": "-fprofile-arcs -ftest-coverage -g -O0",
+        "CMAKE_EXE_LINKER_FLAGS": "-lgcov --coverage",
+        "CMAKE_SHARED_LINKER_FLAGS": "-lgcov --coverage"
+      }
+    },
+    {
+      "name": "dev-coverage",
+      "displayName": "dev with coverage",
+      "description": "Development build with gcov code coverage 
instrumentation",
+      "inherits": ["dev", "coverage"],
+      "binaryDir": "${sourceDir}/build-coverage"
+    },
     {
       "name": "ci",
       "displayName": "CI defaults",
diff --git a/src/test_utils/CMakeLists.txt b/src/test_utils/CMakeLists.txt
new file mode 100644
index 0000000000..339994e283
--- /dev/null
+++ b/src/test_utils/CMakeLists.txt
@@ -0,0 +1,39 @@
+#######################
+#
+#  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.
+#
+#######################
+
+# Test utilities library
+# Provides mock objects and test infrastructure for unit testing
+
+add_library(
+  ats_test_utils STATIC
+  MockIOBuffer.cc
+  TestEventProcessor.cc
+)
+
+target_include_directories(
+  ats_test_utils
+  PUBLIC ${PROJECT_SOURCE_DIR}/include
+         ${PROJECT_SOURCE_DIR}/src
+)
+
+target_link_libraries(
+  ats_test_utils
+  PUBLIC ts::inkevent
+         ts::tscore
+)
+
+add_library(ts::ats_test_utils ALIAS ats_test_utils)
diff --git a/src/test_utils/MockIOBuffer.cc b/src/test_utils/MockIOBuffer.cc
new file mode 100644
index 0000000000..05ab07d0fd
--- /dev/null
+++ b/src/test_utils/MockIOBuffer.cc
@@ -0,0 +1,140 @@
+/** @file
+
+  Mock IOBuffer implementation
+
+  @section license License
+
+  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 "test_utils/MockIOBuffer.h"
+
+MockIOBuffer::MockIOBuffer(int64_t size_index)
+{
+  buffer_ = new_MIOBuffer(size_index);
+  reader_ = buffer_->alloc_reader();
+}
+
+MockIOBuffer::MockIOBuffer(std::string_view data, int64_t size_index) : 
MockIOBuffer(size_index)
+{
+  write(data);
+}
+
+MockIOBuffer::~MockIOBuffer()
+{
+  if (buffer_) {
+    free_MIOBuffer(buffer_);
+  }
+}
+
+MockIOBuffer::MockIOBuffer(MockIOBuffer &&other) noexcept : 
buffer_(other.buffer_), reader_(other.reader_)
+{
+  other.buffer_ = nullptr;
+  other.reader_ = nullptr;
+}
+
+MockIOBuffer &
+MockIOBuffer::operator=(MockIOBuffer &&other) noexcept
+{
+  if (this != &other) {
+    if (buffer_) {
+      free_MIOBuffer(buffer_);
+    }
+    buffer_       = other.buffer_;
+    reader_       = other.reader_;
+    other.buffer_ = nullptr;
+    other.reader_ = nullptr;
+  }
+  return *this;
+}
+
+int64_t
+MockIOBuffer::write(std::string_view data)
+{
+  return write(data.data(), data.size());
+}
+
+int64_t
+MockIOBuffer::write(const void *data, int64_t len)
+{
+  return buffer_->write(data, len);
+}
+
+IOBufferReader *
+MockIOBuffer::reader()
+{
+  return reader_;
+}
+
+std::string
+MockIOBuffer::read_all()
+{
+  std::string result;
+  int64_t     avail = reader_->read_avail();
+  if (avail > 0) {
+    result.resize(avail);
+    reader_->read(result.data(), avail);
+  }
+  return result;
+}
+
+int64_t
+MockIOBuffer::available() const
+{
+  return reader_->read_avail();
+}
+
+void
+MockIOBuffer::reset()
+{
+  reader_->consume(reader_->read_avail());
+}
+
+// MockIOBufferChain implementation
+
+MockIOBufferChain::MockIOBufferChain(std::string_view data, int64_t block_size)
+{
+  buffer_ = new_MIOBuffer(BUFFER_SIZE_INDEX_4K);
+  reader_ = buffer_->alloc_reader();
+
+  // Write data in chunks to create multiple blocks
+  size_t offset = 0;
+  while (offset < data.size()) {
+    size_t chunk_size = std::min(static_cast<size_t>(block_size), data.size() 
- offset);
+    buffer_->write(data.data() + offset, chunk_size);
+    offset += chunk_size;
+  }
+}
+
+MockIOBufferChain::~MockIOBufferChain()
+{
+  if (buffer_) {
+    free_MIOBuffer(buffer_);
+  }
+}
+
+IOBufferReader *
+MockIOBufferChain::reader()
+{
+  return reader_;
+}
+
+MIOBuffer *
+MockIOBufferChain::buffer()
+{
+  return buffer_;
+}
diff --git a/src/test_utils/MockIOBuffer.h b/src/test_utils/MockIOBuffer.h
new file mode 100644
index 0000000000..51e6db3008
--- /dev/null
+++ b/src/test_utils/MockIOBuffer.h
@@ -0,0 +1,146 @@
+/** @file
+
+  Mock IOBuffer utilities for unit testing
+
+  @section license License
+
+  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.
+*/
+
+#pragma once
+
+#include "iocore/eventsystem/IOBuffer.h"
+#include <string>
+#include <string_view>
+
+/**
+ * @class MockIOBuffer
+ * @brief Helper class for creating and managing IOBuffers in tests
+ *
+ * Simplifies the creation and manipulation of IOBuffers for unit testing.
+ * Handles proper allocation and cleanup.
+ *
+ * Usage:
+ * @code
+ * MockIOBuffer buf("test data");
+ * IOBufferReader *reader = buf.reader();
+ * // ... use reader in tests ...
+ * @endcode
+ */
+class MockIOBuffer
+{
+public:
+  /**
+   * Create an empty MockIOBuffer
+   * @param size_index Buffer size index (default: BUFFER_SIZE_INDEX_4K)
+   */
+  explicit MockIOBuffer(int64_t size_index = BUFFER_SIZE_INDEX_4K);
+
+  /**
+   * Create a MockIOBuffer with initial data
+   * @param data Initial data to write to the buffer
+   * @param size_index Buffer size index (default: BUFFER_SIZE_INDEX_4K)
+   */
+  MockIOBuffer(std::string_view data, int64_t size_index = 
BUFFER_SIZE_INDEX_4K);
+
+  ~MockIOBuffer();
+
+  // Non-copyable
+  MockIOBuffer(const MockIOBuffer &)            = delete;
+  MockIOBuffer &operator=(const MockIOBuffer &) = delete;
+
+  // Movable
+  MockIOBuffer(MockIOBuffer &&other) noexcept;
+  MockIOBuffer &operator=(MockIOBuffer &&other) noexcept;
+
+  /**
+   * Write data to the buffer
+   * @param data Data to write
+   * @return Number of bytes written
+   */
+  int64_t write(std::string_view data);
+
+  /**
+   * Write data to the buffer
+   * @param data Pointer to data
+   * @param len Length of data
+   * @return Number of bytes written
+   */
+  int64_t write(const void *data, int64_t len);
+
+  /**
+   * Get a reader for this buffer
+   * @return Pointer to IOBufferReader (owned by the MIOBuffer)
+   */
+  IOBufferReader *reader();
+
+  /**
+   * Get the underlying MIOBuffer
+   * @return Pointer to MIOBuffer
+   */
+  MIOBuffer *
+  buffer()
+  {
+    return buffer_;
+  }
+
+  /**
+   * Read all available data as a string
+   * @return String containing all data in the buffer
+   */
+  std::string read_all();
+
+  /**
+   * Get the number of bytes available to read
+   */
+  int64_t available() const;
+
+  /**
+   * Reset the buffer (clear all data)
+   */
+  void reset();
+
+private:
+  MIOBuffer      *buffer_ = nullptr;
+  IOBufferReader *reader_ = nullptr;
+};
+
+/**
+ * @class MockIOBufferChain
+ * @brief Helper for creating multi-block IOBuffer chains for testing
+ *
+ * Useful for testing code that handles data spanning multiple IOBuffer blocks.
+ */
+class MockIOBufferChain
+{
+public:
+  /**
+   * Create a chain with data split across multiple blocks
+   * @param data Data to write
+   * @param block_size Maximum size per block
+   */
+  MockIOBufferChain(std::string_view data, int64_t block_size);
+
+  ~MockIOBufferChain();
+
+  IOBufferReader *reader();
+  MIOBuffer      *buffer();
+
+private:
+  MIOBuffer      *buffer_ = nullptr;
+  IOBufferReader *reader_ = nullptr;
+};
diff --git a/src/test_utils/TestEventProcessor.cc 
b/src/test_utils/TestEventProcessor.cc
new file mode 100644
index 0000000000..24db51a495
--- /dev/null
+++ b/src/test_utils/TestEventProcessor.cc
@@ -0,0 +1,73 @@
+/** @file
+
+  Test Event Processor implementation
+
+  @section license License
+
+  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 "test_utils/TestEventProcessor.h"
+#include "iocore/eventsystem/IOBuffer.h"
+#include "tscore/ink_memory.h"
+
+TestEventProcessor *TestEventProcessor::instance_ = nullptr;
+
+TestEventProcessor::TestEventProcessor()
+{
+  if (instance_ != nullptr) {
+    // Only one instance allowed at a time
+    ink_release_assert(!"TestEventProcessor already instantiated");
+  }
+  instance_ = this;
+}
+
+TestEventProcessor::~TestEventProcessor()
+{
+  if (running_.load()) {
+    stop();
+  }
+  instance_ = nullptr;
+}
+
+void
+TestEventProcessor::start()
+{
+  if (running_.exchange(true)) {
+    return; // Already running
+  }
+
+  // Initialize IOBuffer system
+  // This is the minimum required for most unit tests
+  init_buffer_allocators(0); // Use default thread ID
+}
+
+void
+TestEventProcessor::stop()
+{
+  if (!running_.exchange(false)) {
+    return; // Already stopped
+  }
+
+  // Cleanup is handled by the IOBuffer system's static destructors
+}
+
+TestEventProcessor *
+TestEventProcessor::instance()
+{
+  return instance_;
+}
diff --git a/src/test_utils/TestEventProcessor.h 
b/src/test_utils/TestEventProcessor.h
new file mode 100644
index 0000000000..febabd125a
--- /dev/null
+++ b/src/test_utils/TestEventProcessor.h
@@ -0,0 +1,115 @@
+/** @file
+
+  Test Event Processor - Controllable event loop for unit testing
+
+  @section license License
+
+  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.
+*/
+
+#pragma once
+
+#include "iocore/eventsystem/EventSystem.h"
+#include "iocore/eventsystem/EThread.h"
+#include "iocore/eventsystem/Tasks.h"
+
+#include <atomic>
+
+/**
+ * @class TestEventProcessor
+ * @brief A simplified event processor for unit testing
+ *
+ * This class provides a controllable event loop that can be used in unit tests
+ * without requiring the full ATS event system infrastructure. It supports:
+ *
+ * - Single-threaded event processing
+ * - Synchronous event dispatch for deterministic testing
+ * - IOBuffer operations without network I/O
+ *
+ * Usage:
+ * @code
+ * TestEventProcessor ep;
+ * ep.start();
+ * // ... run tests ...
+ * ep.stop();
+ * @endcode
+ */
+class TestEventProcessor
+{
+public:
+  TestEventProcessor();
+  ~TestEventProcessor();
+
+  /**
+   * Start the test event processor
+   * Initializes minimal infrastructure needed for IOBuffer operations
+   */
+  void start();
+
+  /**
+   * Stop the test event processor
+   * Cleans up all resources
+   */
+  void stop();
+
+  /**
+   * Check if the event processor is running
+   */
+  bool
+  is_running() const
+  {
+    return running_.load();
+  }
+
+  /**
+   * Get the event processor singleton
+   * Ensures only one test event processor is active
+   */
+  static TestEventProcessor *instance();
+
+private:
+  std::atomic<bool> running_{false};
+  static TestEventProcessor *instance_;
+};
+
+/**
+ * @class TestEventProcessorScope
+ * @brief RAII wrapper for TestEventProcessor
+ *
+ * Automatically starts the event processor on construction and stops on 
destruction.
+ *
+ * Usage:
+ * @code
+ * TEST_CASE("my test") {
+ *   TestEventProcessorScope ep_scope;
+ *   // Event processor is now running
+ *   // ... test code ...
+ * } // Event processor automatically stopped
+ * @endcode
+ */
+class TestEventProcessorScope
+{
+public:
+  TestEventProcessorScope() { ep_.start(); }
+  ~TestEventProcessorScope() { ep_.stop(); }
+
+  TestEventProcessorScope(const TestEventProcessorScope &)            = delete;
+  TestEventProcessorScope &operator=(const TestEventProcessorScope &) = delete;
+
+private:
+  TestEventProcessor ep_;
+};
diff --git a/tools/coverage-report.sh b/tools/coverage-report.sh
new file mode 100755
index 0000000000..23606316ce
--- /dev/null
+++ b/tools/coverage-report.sh
@@ -0,0 +1,171 @@
+#!/bin/bash
+#
+# 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.
+#
+# Generate code coverage report using gcov/lcov
+#
+# Usage: ./tools/coverage-report.sh [--html] [--threshold N]
+#
+# Options:
+#   --html        Generate HTML report (requires genhtml)
+#   --threshold N Fail if coverage is below N% (default: 0, disabled)
+#   --clean       Clean build directory before building
+#   --help        Show this help message
+#
+
+set -e
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
+BUILD_DIR="${PROJECT_ROOT}/build-coverage"
+COVERAGE_DIR="${PROJECT_ROOT}/coverage-report"
+
+GENERATE_HTML=0
+THRESHOLD=0
+CLEAN=0
+
+# Parse arguments
+while [[ $# -gt 0 ]]; do
+    case $1 in
+        --html)
+            GENERATE_HTML=1
+            shift
+            ;;
+        --threshold)
+            THRESHOLD="$2"
+            shift 2
+            ;;
+        --clean)
+            CLEAN=1
+            shift
+            ;;
+        --help)
+            echo "Usage: $0 [--html] [--threshold N] [--clean]"
+            echo ""
+            echo "Options:"
+            echo "  --html        Generate HTML report (requires genhtml)"
+            echo "  --threshold N Fail if coverage is below N% (default: 0, 
disabled)"
+            echo "  --clean       Clean build directory before building"
+            echo "  --help        Show this help message"
+            exit 0
+            ;;
+        *)
+            echo "Unknown option: $1"
+            exit 1
+            ;;
+    esac
+done
+
+# Check for required tools
+check_tool() {
+    if ! command -v "$1" &> /dev/null; then
+        echo "Error: $1 is required but not installed."
+        echo "Install with: $2"
+        exit 1
+    fi
+}
+
+check_tool lcov "brew install lcov (macOS) or apt install lcov (Linux)"
+if [[ $GENERATE_HTML -eq 1 ]]; then
+    check_tool genhtml "brew install lcov (macOS) or apt install lcov (Linux)"
+fi
+
+cd "${PROJECT_ROOT}"
+
+# Clean if requested
+if [[ $CLEAN -eq 1 ]] && [[ -d "${BUILD_DIR}" ]]; then
+    echo "==> Cleaning build directory..."
+    rm -rf "${BUILD_DIR}"
+fi
+
+# Configure
+echo "==> Configuring with coverage preset..."
+cmake --preset dev-coverage
+
+# Build
+echo "==> Building..."
+cmake --build "${BUILD_DIR}" -j "$(nproc 2>/dev/null || sysctl -n hw.ncpu)"
+
+# Run tests
+echo "==> Running tests..."
+ctest --test-dir "${BUILD_DIR}" --output-on-failure || true
+
+# Capture coverage data
+echo "==> Capturing coverage data..."
+lcov --capture \
+    --directory "${BUILD_DIR}" \
+    --output-file "${BUILD_DIR}/coverage.info" \
+    --ignore-errors mismatch
+
+# Remove system headers and test files from coverage
+echo "==> Filtering coverage data..."
+lcov --remove "${BUILD_DIR}/coverage.info" \
+    '/usr/*' \
+    '/opt/*' \
+    '*/unit_tests/*' \
+    '*/test_*' \
+    '*/lib/Catch2/*' \
+    '*/lib/yamlcpp/*' \
+    '*/lib/swoc/*' \
+    --output-file "${BUILD_DIR}/coverage-filtered.info" \
+    --ignore-errors unused
+
+# Generate summary
+echo ""
+echo "==> Coverage Summary:"
+lcov --summary "${BUILD_DIR}/coverage-filtered.info" 2>&1 | tee 
"${BUILD_DIR}/coverage-summary.txt"
+
+# Extract coverage percentage
+COVERAGE=$(lcov --summary "${BUILD_DIR}/coverage-filtered.info" 2>&1 | grep 
"lines" | grep -oP '\d+\.\d+%' | head -1 | tr -d '%')
+
+if [[ -z "$COVERAGE" ]]; then
+    # Try alternative parsing for different lcov versions
+    COVERAGE=$(lcov --summary "${BUILD_DIR}/coverage-filtered.info" 2>&1 | 
grep -oE '[0-9]+\.[0-9]+%' | head -1 | tr -d '%')
+fi
+
+echo ""
+echo "Line coverage: ${COVERAGE:-unknown}%"
+
+# Generate HTML report if requested
+if [[ $GENERATE_HTML -eq 1 ]]; then
+    echo "==> Generating HTML report..."
+    rm -rf "${COVERAGE_DIR}"
+    genhtml "${BUILD_DIR}/coverage-filtered.info" \
+        --output-directory "${COVERAGE_DIR}" \
+        --title "ATS Code Coverage" \
+        --legend \
+        --show-details
+    echo ""
+    echo "HTML report generated at: ${COVERAGE_DIR}/index.html"
+fi
+
+# Check threshold
+if [[ $THRESHOLD -gt 0 ]] && [[ -n "$COVERAGE" ]]; then
+    COVERAGE_INT=${COVERAGE%.*}
+    if [[ $COVERAGE_INT -lt $THRESHOLD ]]; then
+        echo ""
+        echo "ERROR: Coverage ${COVERAGE}% is below threshold ${THRESHOLD}%"
+        exit 1
+    fi
+    echo "Coverage ${COVERAGE}% meets threshold ${THRESHOLD}%"
+fi
+
+echo ""
+echo "==> Coverage report complete!"
+echo "Raw coverage data: ${BUILD_DIR}/coverage.info"
+echo "Filtered coverage: ${BUILD_DIR}/coverage-filtered.info"
+echo "Summary: ${BUILD_DIR}/coverage-summary.txt"

Reply via email to