diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml new file mode 100644 index 000000000..b2cd6e63b --- /dev/null +++ b/.github/workflows/code-coverage.yml @@ -0,0 +1,195 @@ +name: Code Coverage + +on: + push: + branches: [ main, master, develop, yash/impr/NVR_Release_Mem_Leak_FIx_CacheUpdate ] + pull_request: + branches: [ main, master, develop ] + workflow_dispatch: + +jobs: + coverage: + runs-on: ubuntu-latest + env: + CMAKE_TC_FILE: '../vcpkg/scripts/buildsystems/vcpkg.cmake' + ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true + + steps: + - name: Install dependencies + run: | + sudo apt-get update -qq + sudo apt-get -y install \ + ca-certificates curl zip unzip tar autoconf automake autopoint \ + build-essential flex git-core libass-dev libfreetype6-dev \ + libgnutls28-dev libmp3lame-dev libsdl2-dev libtool \ + libsoup-gnome2.4-dev libva-dev libvdpau-dev libvorbis-dev \ + libxdamage-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev \ + libncurses5-dev libncursesw5-dev ninja-build pkg-config texinfo \ + wget yasm zlib1g-dev nasm gperf bison python3 python3-pip \ + dos2unix libx11-dev libgles2-mesa-dev libxinerama-dev \ + libxcursor-dev xorg-dev libglu1-mesa-dev python3-jinja2 \ + lcov gcovr + pip3 install meson + pip3 install Jinja2 + # Install CMake 3.29.6 + pip3 install cmake==3.29.6 || echo 'CMake update skipped' + + - name: Verify tools + run: | + cmake --version + ninja --version + gcc --version + git --version + lcov --version + gcovr --version + + - name: Checkout code + uses: actions/checkout@v3 + with: + submodules: 'recursive' + fetch-depth: 0 + + - name: List Submodules + run: | + git config --global --add safe.directory "*" + git submodule status > submodule_ver.txt + cat submodule_ver.txt + git rev-list --all --count + + - name: Run VCPKG bootstrap + run: ./vcpkg/bootstrap-vcpkg.sh + + - name: Remove CUDA from vcpkg (running without CUDA for coverage) + working-directory: ${{github.workspace}}/base + run: ./fix-vcpkg-json.ps1 -removeCUDA + shell: pwsh + continue-on-error: true + + - name: Create build directory + run: mkdir -p build + + - name: Configure CMake with Coverage + working-directory: ${{github.workspace}}/build + run: | + cmake -B . \ + -DENABLE_WINDOWS=OFF \ + -DENABLE_LINUX=ON \ + -DENABLE_CUDA=OFF \ + -DCODE_COVERAGE=ON \ + -DCMAKE_BUILD_TYPE=Debug \ + -DCMAKE_TOOLCHAIN_FILE=${{env.CMAKE_TC_FILE}} \ + ../base + + - name: Build with Coverage + working-directory: ${{github.workspace}}/build + run: cmake --build . --config Debug -j 4 + + - name: Run Tests and Generate Coverage + working-directory: ${{github.workspace}}/build + run: | + # Run the coverage target which will: + # 1. Reset coverage counters + # 2. Run tests + # 3. Generate lcov report + # 4. Generate HTML report + make coverage || echo 'Coverage generation completed with warnings' + timeout-minutes: 30 + continue-on-error: true + + - name: Generate Coverage Summary + working-directory: ${{github.workspace}}/build + run: | + # Generate a text summary + lcov --summary coverage.info > coverage_summary.txt 2>&1 || true + cat coverage_summary.txt + + # Extract coverage percentage for badge + COVERAGE=$(lcov --summary coverage.info 2>&1 | grep "lines" | awk '{print $2}') + echo "COVERAGE_PERCENTAGE=$COVERAGE" >> $GITHUB_ENV + echo "Coverage: $COVERAGE" + continue-on-error: true + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 + with: + files: ./build/coverage.info + flags: unittests + name: codecov-aprapipes + fail_ci_if_error: false + verbose: true + token: ${{ secrets.CODECOV_TOKEN }} + continue-on-error: true + + - name: Upload coverage reports as artifacts + uses: actions/upload-artifact@v4 + with: + name: coverage-report + path: | + build/coverage/ + build/coverage.info + build/coverage_summary.txt + retention-days: 30 + continue-on-error: true + + - name: Comment PR with Coverage + if: github.event_name == 'pull_request' + uses: actions/github-script@v6 + with: + github-token: ${{secrets.GITHUB_TOKEN}} + script: | + const fs = require('fs'); + let summary = 'Coverage report generation failed'; + try { + summary = fs.readFileSync('build/coverage_summary.txt', 'utf8'); + } catch (e) { + console.log('Could not read coverage summary'); + } + + const output = `#### Code Coverage Report πŸ“Š + +
Coverage Summary + + \`\`\` + ${summary} + \`\`\` + +
+ + πŸ“ Full coverage report available in workflow artifacts. + πŸ” View detailed report: [Codecov Dashboard](https://codecov.io/gh/${{github.repository}}) + + *Pusher: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`; + + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: output + }); + continue-on-error: true + + - name: Generate Coverage Badge + working-directory: ${{github.workspace}}/build + run: | + # Generate a simple coverage badge JSON + COVERAGE_NUM=$(echo $COVERAGE_PERCENTAGE | sed 's/%//') + COLOR="red" + if (( $(echo "$COVERAGE_NUM > 80" | bc -l) )); then + COLOR="brightgreen" + elif (( $(echo "$COVERAGE_NUM > 60" | bc -l) )); then + COLOR="yellow" + elif (( $(echo "$COVERAGE_NUM > 40" | bc -l) )); then + COLOR="orange" + fi + + echo "{\"schemaVersion\": 1, \"label\": \"coverage\", \"message\": \"$COVERAGE_PERCENTAGE\", \"color\": \"$COLOR\"}" > coverage-badge.json + cat coverage-badge.json + continue-on-error: true + + - name: Upload badge data + uses: actions/upload-artifact@v4 + with: + name: coverage-badge + path: build/coverage-badge.json + retention-days: 90 + continue-on-error: true diff --git a/COVERAGE_SETUP.md b/COVERAGE_SETUP.md new file mode 100644 index 000000000..931e6067c --- /dev/null +++ b/COVERAGE_SETUP.md @@ -0,0 +1,275 @@ +# Code Coverage Setup - Quick Reference + +This document provides a quick reference for the code coverage setup that has been added to ApraPipes. + +## What Was Added + +### 1. CMake Coverage Support +- **File**: `cmake/CodeCoverage.cmake` + - Complete CMake module for code coverage + - Supports gcov/lcov toolchain + - Provides `setup_target_for_coverage_lcov()` function + +- **Modified**: `base/CMakeLists.txt` + - Added `CODE_COVERAGE` option (default: OFF) + - Integrated CodeCoverage.cmake module + - Created `coverage` make target + - Configured exclusion patterns + +### 2. CI/CD Integration +- **File**: `.github/workflows/code-coverage.yml` + - Automated coverage generation on push/PR + - Runs on: main, master, develop branches + - Uploads to Codecov + - Creates artifacts (HTML reports, coverage.info) + - Posts PR comments with coverage summary + +### 3. Local Development Tools +- **Script**: `generate_coverage.sh` + - One-command coverage generation + - Automatic dependency checking + - Clean build support + - HTML report generation + +### 4. Configuration Files +- **File**: `codecov.yml` + - Codecov service configuration + - Coverage thresholds and targets + - Exclusion patterns + - PR comment formatting + +### 5. Documentation +- **File**: `docs/CODE_COVERAGE.md` + - Complete user guide + - Prerequisites and setup + - Troubleshooting section + - Best practices + - Advanced usage examples + +- **Modified**: `README.md` + - Added coverage badges + - Quick links to coverage reports + - Link to detailed documentation + +## Quick Start + +### For Developers (Local) + +```bash +# Generate coverage report locally +./generate_coverage.sh + +# View the report +xdg-open build_coverage/coverage/index.html +``` + +### For CI/CD + +Coverage is automatically generated and published on every push/PR. No manual action needed! + +**View Coverage:** +- [Codecov Dashboard](https://codecov.io/gh/Apra-Labs/ApraPipes) +- GitHub Actions β†’ Coverage Workflow β†’ Artifacts + +### For Manual CMake Build + +```bash +mkdir -p build_coverage && cd build_coverage + +cmake -B . \ + -DENABLE_LINUX=ON \ + -DENABLE_CUDA=OFF \ + -DCODE_COVERAGE=ON \ + -DCMAKE_BUILD_TYPE=Debug \ + -DCMAKE_TOOLCHAIN_FILE=../vcpkg/scripts/buildsystems/vcpkg.cmake \ + ../base + +cmake --build . --config Debug -j $(nproc) +make coverage +``` + +## File Structure + +``` +ApraPipes/ +β”œβ”€β”€ cmake/ +β”‚ └── CodeCoverage.cmake # CMake coverage module +β”œβ”€β”€ .github/workflows/ +β”‚ └── code-coverage.yml # GitHub Actions workflow +β”œβ”€β”€ docs/ +β”‚ └── CODE_COVERAGE.md # Detailed documentation +β”œβ”€β”€ base/ +β”‚ └── CMakeLists.txt # Modified for coverage +β”œβ”€β”€ codecov.yml # Codecov configuration +β”œβ”€β”€ generate_coverage.sh # Local coverage script +β”œβ”€β”€ COVERAGE_SETUP.md # This file +└── README.md # Updated with badges +``` + +## Prerequisites + +### Ubuntu/Debian +```bash +sudo apt-get install lcov gcov gcovr +``` + +### Build Dependencies +Standard ApraPipes build dependencies (see main README.md) + +## Coverage Exclusions + +The following are automatically excluded from coverage: +- Test files (`base/test/*`) +- Third-party libraries (`thirdparty/*`) +- VCPKG packages (`vcpkg/*`) +- Build artifacts (`build/*`) +- System headers (`/usr/*`) +- Boost headers +- OpenCV headers + +## Integration Points + +### Codecov Setup (Optional but Recommended) + +1. Visit https://codecov.io +2. Sign in with GitHub +3. Add the ApraPipes repository +4. Get the upload token +5. Add to GitHub Secrets as `CODECOV_TOKEN` + +Once configured, every PR will show: +- Coverage percentage +- Coverage diff +- Line-by-line coverage in PR view + +### IDE Integration + +#### VS Code +1. Install "Coverage Gutters" extension +2. Generate coverage: `./generate_coverage.sh` +3. Extension auto-detects `coverage.info` + +#### CLion +1. Build with coverage enabled +2. Run β†’ Show Coverage Data +3. Load `build_coverage/coverage.info` + +## Usage Scenarios + +### Scenario 1: Pre-Commit Check +```bash +# Before committing, check coverage +./generate_coverage.sh +# Review uncovered lines in HTML report +xdg-open build_coverage/coverage/index.html +``` + +### Scenario 2: PR Review +- CI automatically generates coverage +- View coverage diff in PR comments +- Check Codecov link for detailed analysis + +### Scenario 3: Coverage Investigation +```bash +# Generate coverage +./generate_coverage.sh + +# Check summary +lcov --summary build_coverage/coverage.info + +# View specific file +xdg-open build_coverage/coverage/base/src/Module.cpp.gcov.html +``` + +### Scenario 4: Module-Specific Coverage +```bash +# Filter for specific modules +cd build_coverage +lcov --capture --directory . \ + --output-file coverage_mp4.info \ + --include "*/Mp4*.cpp" + +genhtml coverage_mp4.info --output-directory coverage_mp4 +xdg-open coverage_mp4/index.html +``` + +## Troubleshooting + +### Issue: Zero coverage reported +**Solution**: Ensure you built with `-DCODE_COVERAGE=ON` and `-DCMAKE_BUILD_TYPE=Debug` + +### Issue: Tests not running +**Solution**: Check test executable: `./build_coverage/aprapipesut --log_level=all` + +### Issue: Old coverage data +**Solution**: Clean and rebuild: +```bash +rm -rf build_coverage +./generate_coverage.sh +``` + +### Issue: Missing dependencies +**Solution**: Install coverage tools: +```bash +sudo apt-get install lcov gcov +``` + +## Next Steps + +1. **Enable Codecov** + - Add `CODECOV_TOKEN` to GitHub secrets + - First run will establish baseline + +2. **Review Initial Coverage** + - Check which modules have low coverage + - Prioritize critical components + +3. **Improve Coverage** + - Add tests for uncovered code + - Focus on core functionality first + - Target 70%+ line coverage + +4. **Monitor Trends** + - Review Codecov graphs weekly + - Watch for coverage drops in PRs + - Celebrate coverage improvements! + +## Support + +- **Full Documentation**: `docs/CODE_COVERAGE.md` +- **Issues**: GitHub Issues +- **Questions**: Contact maintainers + +## Coverage Targets + +| Metric | Target | Good | Excellent | +|--------|--------|------|-----------| +| Line Coverage | > 60% | > 70% | > 80% | +| Function Coverage | > 50% | > 65% | > 75% | +| Branch Coverage | > 40% | > 55% | > 70% | + +**Note**: Focus on critical paths. 100% coverage is rarely necessary. + +## Maintenance + +### Regular Tasks +- Review coverage reports monthly +- Update exclusion patterns as needed +- Keep documentation current +- Monitor CI performance + +### When to Update +- Adding new modules: Update exclusions if needed +- New dependencies: May need new exclude patterns +- CMake changes: Verify coverage build still works + +## Credits + +- Coverage module based on [codecov/example-cpp](https://github.com/codecov/example-cpp) +- CMake integration inspired by [larsbilke/CMake-codecov](https://github.com/bilke/cmake-modules) + +--- + +**Setup Date**: 2025-11-26 +**Last Updated**: 2025-11-26 +**Maintainer**: ApraPipes Development Team diff --git a/README.md b/README.md index 34d36d94b..ea643d538 100755 --- a/README.md +++ b/README.md @@ -18,6 +18,14 @@ Aprapipes is automatically built and tested on Ubuntu (18.04 and 20.04), Jetson |Ubuntu x64_86-WSL|20.04|Yes|[![Test Results](https://gist.githubusercontent.com/kumaakh/f80af234a4aabedc69af3ee197f66944/raw/badge_WSL.svg)](https://gist.githubusercontent.com/kumaakh/f80af234a4aabedc69af3ee197f66944/raw/badge_WSL.svg)|[![CI-Linux-CUDA-wsl](https://github.com/Apra-Labs/ApraPipes/actions/workflows/CI-Linux-CUDA-wsl.yml/badge.svg)](https://github.com/Apra-Labs/ApraPipes/actions/workflows/CI-Linux-CUDA-wsl.yml)| |Ubuntu x64_86-docker|18.04|Yes|No|[![CI-Linux-CUDA-Docker](https://github.com/Apra-Labs/ApraPipes/actions/workflows/CI-Linux-CUDA-Docker.yml/badge.svg)](https://github.com/Apra-Labs/ApraPipes/actions/workflows/CI-Linux-CUDA-Docker.yml)| +## Code Coverage +[![codecov](https://codecov.io/gh/Apra-Labs/ApraPipes/branch/master/graph/badge.svg)](https://codecov.io/gh/Apra-Labs/ApraPipes) +[![Code Coverage](https://github.com/Apra-Labs/ApraPipes/actions/workflows/code-coverage.yml/badge.svg)](https://github.com/Apra-Labs/ApraPipes/actions/workflows/code-coverage.yml) + +Code coverage is automatically generated and published on every commit. View detailed coverage reports on [Codecov](https://codecov.io/gh/Apra-Labs/ApraPipes) or download artifacts from the [Coverage Workflow](https://github.com/Apra-Labs/ApraPipes/actions/workflows/code-coverage.yml). + +For local coverage generation, see [Code Coverage Documentation](docs/CODE_COVERAGE.md). + ## Getting Started with ApraPipes
diff --git a/base/CMakeLists.txt b/base/CMakeLists.txt index 1c964a2b7..333f3704a 100755 --- a/base/CMakeLists.txt +++ b/base/CMakeLists.txt @@ -36,6 +36,25 @@ set(CMAKE_CXX_STANDARD 17) project(APRAPIPES) +# Code Coverage Configuration +OPTION(CODE_COVERAGE "Enable coverage reporting" OFF) +if(CODE_COVERAGE AND ENABLE_LINUX) + message(STATUS "Code coverage enabled") + include(../cmake/CodeCoverage.cmake) + append_coverage_compiler_flags() + + # Exclude patterns for coverage + set(COVERAGE_EXCLUDES + '*/test/*' + '*/thirdparty/*' + '*/vcpkg/*' + '*/build/*' + '/usr/*' + '*/boost/*' + '*/opencv*' + ) +endif() + message(STATUS $ENV{PKG_CONFIG_PATH}">>>>>> PKG_CONFIG_PATH") find_package(PkgConfig REQUIRED) @@ -670,8 +689,8 @@ IF(ENABLE_LINUX) ) ENDIF(ENABLE_LINUX) -target_link_libraries(aprapipesut - aprapipes +target_link_libraries(aprapipesut + aprapipes ${GLEW_LIBRARIES} ${JPEG_LIBRARIES} ${LIBMP4_LIB} @@ -685,7 +704,7 @@ target_link_libraries(aprapipesut ${NVJPEGLIB_L4T} ${CURSES_LIBRARIES} ZXing::Core - ZXing::ZXing + ZXing::ZXing BZip2::BZip2 ZLIB::ZLIB LibLZMA::LibLZMA @@ -694,6 +713,21 @@ target_link_libraries(aprapipesut whisper::whisper ) +# Setup code coverage target +if(CODE_COVERAGE AND ENABLE_LINUX) + setup_target_for_coverage_lcov( + NAME coverage + EXECUTABLE aprapipesut + EXECUTABLE_ARGS --log_level=all + DEPENDENCIES aprapipesut aprapipes + EXCLUDE ${COVERAGE_EXCLUDES} + LCOV_ARGS --no-external + GENHTML_ARGS --legend --title "ApraPipes Code Coverage" + ) + + message(STATUS "Code coverage target 'coverage' created. Run 'make coverage' to generate coverage report.") +endif() + IF(ENABLE_WINDOWS) file(COPY ${RUNTIME_DLLS} DESTINATION Debug/) file(COPY ${RUNTIME_DLLS} DESTINATION Release/) diff --git a/cmake/CodeCoverage.cmake b/cmake/CodeCoverage.cmake new file mode 100644 index 000000000..b09bd5766 --- /dev/null +++ b/cmake/CodeCoverage.cmake @@ -0,0 +1,258 @@ +# Copyright (c) 2012 - 2017, Lars Bilke +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors +# may be used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# CHANGES: +# +# 2012-01-31, Lars Bilke +# - Enable Code Coverage +# +# 2013-09-17, Joakim SΓΆderberg +# - Added support for Clang. +# - Some additional usage instructions. +# +# 2016-02-03, Lars Bilke +# - Refactored functions to use named parameters +# +# 2017-06-02, Lars Bilke +# - Merged with modified version from github.com/ufz/ogs +# +# 2019-05-06, Anatolii Kurotych +# - Remove unnecessary --coverage flag +# +# 2019-12-13, FeRD (Frank Dana) +# - Deprecate COVERAGE_LCOVR_EXCLUDES and COVERAGE_GCOVR_EXCLUDES lists in favor +# of tool-agnostic COVERAGE_EXCLUDES variable +# +# 2020-01-19, Mike Purvis +# - Add genhtml options to customise HTML output +# +# 2020-03-10, Martin Grap +# - Add option to create XML coverage reports +# +# USAGE: +# +# 1. Copy this file into your cmake modules path. +# +# 2. Add the following line to your CMakeLists.txt (best before any add_executable() +# or add_library() calls): +# include(CodeCoverage) +# +# 3. Append necessary compiler flags for code coverage: +# append_coverage_compiler_flags() +# +# 3.a (OPTIONAL) Set appropriate optimization flags, e.g. -O0, -O1 or -Og +# +# 4. If you need to exclude additional directories from the report, specify them +# using full paths in the COVERAGE_EXCLUDES variable before calling +# setup_target_for_coverage_*(). +# Example: +# set(COVERAGE_EXCLUDES +# '${PROJECT_SOURCE_DIR}/tests/*' +# '/path/to/my/directory/*') +# Or, use the EXCLUDE argument to setup_target_for_coverage_*(). +# Example: +# setup_target_for_coverage_lcov( +# NAME coverage +# EXECUTABLE testrunner +# EXCLUDE "${PROJECT_SOURCE_DIR}/tests/*") +# +# 5. Use the functions described below to create a custom make target which +# runs your test executable and produces a code coverage report. +# +# 6. Build a Debug build: +# cmake -DCMAKE_BUILD_TYPE=Debug .. +# make +# make my_coverage_target +# + +include(CMakeParseArguments) + +option(CODE_COVERAGE "Enable coverage reporting" OFF) + +if(CODE_COVERAGE AND NOT CODE_COVERAGE_ADDED) + set(CODE_COVERAGE_ADDED 1) + + # Check prereqs + find_program(GCOV_PATH gcov) + find_program(LCOV_PATH NAMES lcov lcov.bat lcov.exe lcov.perl) + find_program(GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat) + find_program(GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test) + + if(NOT GCOV_PATH) + message(FATAL_ERROR "gcov not found! Aborting...") + endif() + + if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang") + if(CMAKE_C_COMPILER_VERSION VERSION_LESS 3) + message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...") + endif() + elseif(NOT CMAKE_COMPILER_IS_GNUCXX) + if(NOT CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang") + message(FATAL_ERROR "Compiler is not GNU gcc or Clang! Aborting...") + endif() + endif() + + set(COVERAGE_COMPILER_FLAGS "-g -fprofile-arcs -ftest-coverage" + CACHE INTERNAL "") + + set(CMAKE_C_FLAGS_COVERAGE + ${COVERAGE_COMPILER_FLAGS} + CACHE STRING "Flags used by the C compiler during coverage builds." + FORCE) + set(CMAKE_CXX_FLAGS_COVERAGE + ${COVERAGE_COMPILER_FLAGS} + CACHE STRING "Flags used by the C++ compiler during coverage builds." + FORCE) + set(CMAKE_EXE_LINKER_FLAGS_COVERAGE + "" + CACHE STRING "Flags used for linking binaries during coverage builds." + FORCE) + set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE + "" + CACHE STRING "Flags used by the shared libraries linker during coverage builds." + FORCE) + mark_as_advanced( + CMAKE_C_FLAGS_COVERAGE + CMAKE_CXX_FLAGS_COVERAGE + CMAKE_EXE_LINKER_FLAGS_COVERAGE + CMAKE_SHARED_LINKER_FLAGS_COVERAGE) + + if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang") + set(CMAKE_C_FLAGS_COVERAGE "${CMAKE_C_FLAGS_COVERAGE} -fprofile-instr-generate -fcoverage-mapping") + set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_COVERAGE} -fprofile-instr-generate -fcoverage-mapping") + endif() + + if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") + message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading") + endif() + + if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + link_libraries(gcov) + endif() + + # Defines a target for running and collection code coverage information + # Builds dependencies, runs the given executable and outputs reports. + # NOTE! The executable should always have a ZERO as exit code otherwise + # the coverage generation will not complete. + # + # setup_target_for_coverage_lcov( + # NAME testrunner_coverage # New target name + # EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR + # DEPENDENCIES testrunner # Dependencies to build first + # EXCLUDE "tests/*" # Patterns to exclude (can be relative to BASE_DIRECTORY) + # BASE_DIRECTORY "../" # Base directory for report + # NO_DEMANGLE # Don't demangle C++ symbols + # LCOV_ARGS "--no-external" # Extra arguments for lcov + # GENHTML_ARGS "--legend" # Extra arguments for genhtml + # ) + function(setup_target_for_coverage_lcov) + set(options NO_DEMANGLE) + set(oneValueArgs BASE_DIRECTORY NAME) + set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES LCOV_ARGS GENHTML_ARGS) + cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT LCOV_PATH) + message(FATAL_ERROR "lcov not found! Aborting...") + endif() + + if(NOT GENHTML_PATH) + message(FATAL_ERROR "genhtml not found! Aborting...") + endif() + + # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR + if(${Coverage_BASE_DIRECTORY}) + get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) + else() + set(BASEDIR ${PROJECT_SOURCE_DIR}) + endif() + + # Collect excludes (CMake 3.4+: Also compute absolute paths) + set(LCOV_EXCLUDES "") + foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_LCOV_EXCLUDES}) + if(CMAKE_VERSION VERSION_GREATER "3.4") + get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR}) + endif() + list(APPEND LCOV_EXCLUDES "${EXCLUDE}") + endforeach() + list(REMOVE_DUPLICATES LCOV_EXCLUDES) + + # Conditional arguments + if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE}) + set(GENHTML_EXTRA_ARGS "--demangle-cpp") + endif() + + # Setup target + add_custom_target(${Coverage_NAME} + # Cleanup lcov + COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --directory . --zerocounters + # Create baseline to make sure untouched files show up in the report + COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --directory . --capture --initial --output-file ${Coverage_NAME}.base + # Run tests + COMMAND ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS} + # Capturing lcov counters and generating report + COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --directory . --capture --output-file ${Coverage_NAME}.capture + # add baseline counters + COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --add-tracefile ${Coverage_NAME}.base --add-tracefile ${Coverage_NAME}.capture --output-file ${Coverage_NAME}.total + # filter collected data to final coverage report + COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --remove ${Coverage_NAME}.total ${LCOV_EXCLUDES} --output-file ${Coverage_NAME}.info + # Generate HTML output + COMMAND ${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS} -o ${Coverage_NAME} ${Coverage_NAME}.info + # Cleanup + COMMAND ${CMAKE_COMMAND} -E remove ${Coverage_NAME}.base ${Coverage_NAME}.capture ${Coverage_NAME}.total + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + DEPENDS ${Coverage_DEPENDENCIES} + COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report." + ) + + # Show where to find the lcov info report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Lcov code coverage info report saved in ${Coverage_NAME}.info." + ) + + # Show info where to find the report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Open ${PROJECT_BINARY_DIR}/${Coverage_NAME}/index.html in your browser to view the coverage report." + ) + + endfunction() + + function(append_coverage_compiler_flags) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) + message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}") + endfunction() + + function(append_coverage_compiler_flags_to_target name) + separate_arguments(_flag_list NATIVE_COMMAND "${COVERAGE_COMPILER_FLAGS}") + target_compile_options(${name} PRIVATE ${_flag_list}) + target_link_libraries(${name} PRIVATE gcov) + endfunction() + +endif() diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 000000000..6841f03f4 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,54 @@ +# Codecov Configuration for ApraPipes +# Documentation: https://docs.codecov.com/docs/codecov-yaml + +codecov: + require_ci_to_pass: yes + notify: + wait_for_ci: yes + +coverage: + precision: 2 + round: down + range: "60...90" + + status: + project: + default: + target: auto + threshold: 1% + base: auto + if_ci_failed: error + + patch: + default: + target: 70% + threshold: 5% + if_ci_failed: error + + changes: no + +comment: + layout: "header, diff, files, footer" + behavior: default + require_changes: no + require_base: no + require_head: yes + +ignore: + - "base/test/**/*" + - "thirdparty/**/*" + - "vcpkg/**/*" + - "build/**/*" + - "**/*.md" + - "docs/**/*" + +flags: + unittests: + paths: + - base/src/ + carryforward: true + +github_checks: + annotations: true + +slack_app: off diff --git a/docs/CODE_COVERAGE.md b/docs/CODE_COVERAGE.md new file mode 100644 index 000000000..1ff4a24d3 --- /dev/null +++ b/docs/CODE_COVERAGE.md @@ -0,0 +1,400 @@ +# ApraPipes Code Coverage + +This document describes how to generate and view code coverage reports for the ApraPipes project. + +## Table of Contents + +- [Overview](#overview) +- [Prerequisites](#prerequisites) +- [Quick Start](#quick-start) +- [Manual Coverage Generation](#manual-coverage-generation) +- [CI/CD Integration](#cicd-integration) +- [Viewing Coverage Reports](#viewing-coverage-reports) +- [Understanding Coverage Metrics](#understanding-coverage-metrics) +- [Troubleshooting](#troubleshooting) + +## Overview + +Code coverage measures how much of the codebase is executed during testing. The ApraPipes project uses: + +- **gcov**: GNU coverage tool for instrumenting code +- **lcov**: Test coverage program that uses gcov +- **genhtml**: Generates HTML coverage reports from lcov data + +Coverage reports help identify: +- Untested code paths +- Areas needing more tests +- Overall test quality + +## Prerequisites + +### Linux (Ubuntu/Debian) + +```bash +sudo apt-get update +sudo apt-get install -y lcov gcov gcovr +``` + +### Required Build Tools + +Ensure you have the standard ApraPipes build dependencies installed: + +```bash +sudo apt-get install -y \ + build-essential cmake ninja-build \ + git pkg-config python3 python3-pip +``` + +## Quick Start + +### Using the Helper Script + +The easiest way to generate coverage is using the provided script: + +```bash +# From the project root directory +./generate_coverage.sh +``` + +For a clean build: + +```bash +./generate_coverage.sh clean +``` + +This script will: +1. Configure CMake with coverage enabled +2. Build the project with instrumentation +3. Run all tests +4. Generate coverage reports (both .info and HTML) +5. Display a summary + +### Opening the Report + +After generation, open the HTML report: + +```bash +# Linux +xdg-open build_coverage/coverage/index.html + +# Or with Firefox +firefox build_coverage/coverage/index.html +``` + +## Manual Coverage Generation + +If you prefer manual control: + +### Step 1: Configure Build + +```bash +mkdir -p build_coverage +cd build_coverage + +cmake -B . \ + -DENABLE_LINUX=ON \ + -DENABLE_CUDA=OFF \ + -DCODE_COVERAGE=ON \ + -DCMAKE_BUILD_TYPE=Debug \ + -DCMAKE_TOOLCHAIN_FILE=../vcpkg/scripts/buildsystems/vcpkg.cmake \ + ../base +``` + +### Step 2: Build with Coverage + +```bash +cmake --build . --config Debug -j $(nproc) +``` + +### Step 3: Run Tests and Generate Report + +```bash +make coverage +``` + +This target automatically: +- Resets coverage counters +- Runs the test suite (`aprapipesut`) +- Collects coverage data +- Filters out system/third-party libraries +- Generates HTML report in `coverage/` directory + +### Step 4: View Coverage Summary + +```bash +lcov --summary coverage.info +``` + +## CI/CD Integration + +### GitHub Actions + +Coverage is automatically generated on: +- Push to `main`, `master`, `develop` branches +- Pull requests to these branches +- Manual workflow dispatch + +### Workflow File + +`.github/workflows/code-coverage.yml` + +### Artifacts + +Coverage reports are uploaded as workflow artifacts: +- HTML coverage report +- `coverage.info` lcov file +- Coverage summary text +- Coverage badge JSON + +Download from: GitHub Actions β†’ Workflow Run β†’ Artifacts + +### Codecov Integration + +Coverage data is automatically uploaded to [Codecov](https://codecov.io) for trend tracking and PR comments. + +**Setup Codecov:** + +1. Sign up at https://codecov.io with your GitHub account +2. Add your repository +3. Get the upload token +4. Add as GitHub secret: `CODECOV_TOKEN` + +## Viewing Coverage Reports + +### HTML Report Structure + +``` +coverage/ +β”œβ”€β”€ index.html # Main page with overall summary +β”œβ”€β”€ base/ +β”‚ β”œβ”€β”€ src/ # Source file coverage +β”‚ β”‚ β”œβ”€β”€ Module.cpp.gcov.html +β”‚ β”‚ └── ... +β”‚ └── include/ # Header file coverage +└── ... +``` + +### Report Features + +- **Line Coverage**: Which lines were executed +- **Function Coverage**: Which functions were called +- **Branch Coverage**: Which conditional branches were taken +- **Color Coding**: + - 🟒 Green: Covered lines + - πŸ”΄ Red: Uncovered lines + - 🟑 Yellow: Partially covered branches + +### Key Files to Review + +Focus coverage improvements on: +- Core pipeline modules (`src/Module.cpp`, `src/Pipeline.cpp`) +- Critical components (`src/Mp4ReaderSource.cpp`, `src/Mp4WriterSink.cpp`) +- Cache management (`src/OrderedCacheOfFiles.cpp`) +- Frame processing modules + +## Understanding Coverage Metrics + +### Line Coverage + +``` +Lines......: 75.2% (8543 of 11352 lines) +``` + +Percentage of code lines executed during tests. + +### Function Coverage + +``` +Functions..: 68.4% (1234 of 1804 functions) +``` + +Percentage of functions called during tests. + +### Branch Coverage + +``` +Branches...: 52.1% (3421 of 6567 branches) +``` + +Percentage of conditional branches (if/else, switch) taken. + +### Coverage Goals + +- **Good**: > 70% line coverage +- **Excellent**: > 80% line coverage +- **Outstanding**: > 90% line coverage + +**Note**: 100% coverage is often impractical. Focus on critical paths. + +## Excluding Code from Coverage + +### In CMakeLists.txt + +Excluded patterns are defined in `base/CMakeLists.txt`: + +```cmake +set(COVERAGE_EXCLUDES + '*/test/*' # Test files themselves + '*/thirdparty/*' # Third-party libraries + '*/vcpkg/*' # Package manager files + '*/build/*' # Build artifacts + '/usr/*' # System libraries + '*/boost/*' # Boost headers + '*/opencv*' # OpenCV headers +) +``` + +### In Code + +Use `LCOV_EXCL` markers to exclude specific lines: + +```cpp +// LCOV_EXCL_START +void debugOnlyFunction() { + // This won't be counted in coverage +} +// LCOV_EXCL_STOP + +// Or single line: +unreachablecode(); // LCOV_EXCL_LINE +``` + +## Troubleshooting + +### Issue: "gcov not found" + +```bash +sudo apt-get install gcc gcov +``` + +### Issue: "lcov not found" + +```bash +sudo apt-get install lcov +``` + +### Issue: Zero coverage reported + +**Causes:** +1. Tests didn't run successfully +2. Wrong build type (must be Debug) +3. CODE_COVERAGE flag not set + +**Solution:** + +```bash +# Check test execution +./build_coverage/aprapipesut --log_level=all + +# Verify build flags +cmake -L build_coverage/ | grep COVERAGE +# Should show: CODE_COVERAGE:BOOL=ON +``` + +### Issue: Coverage data from old runs + +```bash +# Clean coverage data +cd build_coverage +lcov --directory . --zerocounters + +# Or rebuild from scratch +cd .. +rm -rf build_coverage +./generate_coverage.sh +``` + +### Issue: HTML report not generated + +```bash +# Check for genhtml +which genhtml + +# Manually generate HTML +cd build_coverage +genhtml coverage.info --output-directory coverage +``` + +### Issue: CUDA code showing in coverage + +CUDA code is excluded by building with `ENABLE_CUDA=OFF` for coverage. This is intentional as: +- CUDA code coverage requires different tools +- Most CUDA code is vendor-specific +- Focus on host-side logic first + +## Advanced Usage + +### Filtering Coverage by Pattern + +```bash +# Generate coverage only for specific modules +lcov --capture --directory . \ + --output-file coverage_filtered.info \ + --include "*/src/Module.cpp" \ + --include "*/src/Pipeline.cpp" + +genhtml coverage_filtered.info --output-directory coverage_modules +``` + +### Coverage Diff Between Branches + +```bash +# Generate baseline coverage (main branch) +git checkout main +./generate_coverage.sh +cp build_coverage/coverage.info coverage_main.info + +# Generate feature coverage +git checkout feature-branch +./generate_coverage.sh clean + +# Compare +lcov --diff coverage_main.info build_coverage/coverage.info \ + --output-file coverage_diff.info + +genhtml coverage_diff.info --output-directory coverage_diff +``` + +### Integration with IDEs + +#### Visual Studio Code + +Install extension: "Coverage Gutters" + +1. Generate `coverage.info` +2. Open VS Code in project root +3. Extension automatically shows coverage in editor + +#### CLion / IntelliJ + +1. Build with coverage: `make coverage` +2. Run β†’ Show Coverage Data +3. Load `build_coverage/coverage.info` + +## Best Practices + +1. **Run coverage locally before PR**: Catch untested code early +2. **Focus on critical paths**: 100% coverage isn't always necessary +3. **Review uncovered lines**: Understand why they're not tested +4. **Add tests incrementally**: Don't aim for perfection immediately +5. **Check coverage trends**: Monitor coverage over time +6. **Exclude intentionally**: Mark debug/unreachable code explicitly + +## Additional Resources + +- [gcov Documentation](https://gcc.gnu.org/onlinedocs/gcc/Gcov.html) +- [lcov README](http://ltp.sourceforge.net/coverage/lcov.php) +- [Codecov Documentation](https://docs.codecov.com/) +- [Code Coverage Best Practices](https://testing.googleblog.com/2020/08/code-coverage-best-practices.html) + +## Support + +For issues or questions: +- Open an issue on GitHub +- Check existing coverage-related issues +- Contact the development team + +--- + +**Last Updated**: 2025-11-26 +**Maintainer**: ApraPipes Development Team diff --git a/generate_coverage.sh b/generate_coverage.sh new file mode 100755 index 000000000..91c7c9a2e --- /dev/null +++ b/generate_coverage.sh @@ -0,0 +1,92 @@ +#!/bin/bash +# +# ApraPipes Code Coverage Generation Script +# This script builds the project with coverage enabled and generates an HTML coverage report +# +# Usage: +# ./generate_coverage.sh [clean] +# +# Options: +# clean - Perform a clean build (removes existing build directory) +# + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Script directory +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +BUILD_DIR="${SCRIPT_DIR}/build_coverage" + +echo -e "${BLUE}========================================${NC}" +echo -e "${BLUE} ApraPipes Code Coverage Generator${NC}" +echo -e "${BLUE}========================================${NC}" + +# Check if clean build is requested +if [ "$1" == "clean" ]; then + echo -e "${YELLOW}Performing clean build...${NC}" + if [ -d "$BUILD_DIR" ]; then + rm -rf "$BUILD_DIR" + echo -e "${GREEN}Build directory cleaned.${NC}" + fi +fi + +# Check for required tools +echo -e "\n${BLUE}Checking for required tools...${NC}" + +command -v cmake >/dev/null 2>&1 || { echo -e "${RED}Error: cmake is required but not installed.${NC}" >&2; exit 1; } +command -v lcov >/dev/null 2>&1 || { echo -e "${RED}Error: lcov is required but not installed. Install with: sudo apt-get install lcov${NC}" >&2; exit 1; } +command -v genhtml >/dev/null 2>&1 || { echo -e "${RED}Error: genhtml is required but not installed. Install with: sudo apt-get install lcov${NC}" >&2; exit 1; } +command -v gcov >/dev/null 2>&1 || { echo -e "${RED}Error: gcov is required but not installed.${NC}" >&2; exit 1; } + +echo -e "${GREEN}All required tools found.${NC}" + +# Create build directory +mkdir -p "$BUILD_DIR" +cd "$BUILD_DIR" + +# Configure CMake with coverage +echo -e "\n${BLUE}Configuring CMake with code coverage enabled...${NC}" +cmake -B . \ + -DENABLE_WINDOWS=OFF \ + -DENABLE_LINUX=ON \ + -DENABLE_CUDA=OFF \ + -DCODE_COVERAGE=ON \ + -DCMAKE_BUILD_TYPE=Debug \ + -DCMAKE_TOOLCHAIN_FILE=../vcpkg/scripts/buildsystems/vcpkg.cmake \ + ../base + +# Build the project +echo -e "\n${BLUE}Building project with coverage instrumentation...${NC}" +cmake --build . --config Debug -j $(nproc) + +# Run coverage target +echo -e "\n${BLUE}Running tests and generating coverage report...${NC}" +make coverage + +# Display summary +echo -e "\n${GREEN}========================================${NC}" +echo -e "${GREEN} Coverage Report Generated!${NC}" +echo -e "${GREEN}========================================${NC}" + +if [ -f "coverage.info" ]; then + echo -e "\n${BLUE}Coverage Summary:${NC}" + lcov --summary coverage.info +fi + +if [ -d "coverage" ]; then + REPORT_PATH="${BUILD_DIR}/coverage/index.html" + echo -e "\n${GREEN}HTML Report Location:${NC}" + echo -e " ${REPORT_PATH}" + echo -e "\n${YELLOW}Open the report with:${NC}" + echo -e " xdg-open ${REPORT_PATH}" + echo -e " or" + echo -e " firefox ${REPORT_PATH}" +fi + +echo -e "\n${GREEN}Done!${NC}\n"