diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 283e2981..7fbf7627 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,7 +27,7 @@ jobs: - name: Install Ubuntu dependencies run: | sudo apt-get update - sudo apt-get install -y libgtest-dev nlohmann-json3-dev pybind11-dev python3-pip ninja-build + sudo apt-get install -y libgtest-dev nlohmann-json3-dev pybind11-dev python3-pip ninja-build lcov - name: Install Clang if: matrix.compiler == 'clang' @@ -37,7 +37,7 @@ jobs: - name: Build Release run: | - cmake -B build -S . \ + cmake -B build-release -S . \ -DCMAKE_C_COMPILER="${CC}" \ -DCMAKE_CXX_COMPILER="${CXX}" \ -DCMAKE_BUILD_TYPE=Release \ @@ -46,9 +46,9 @@ jobs: -DBUILD_LEGACY_API=OFF \ -DCMAKE_INSTALL_PREFIX=install-release - cmake --build build -j "$(nproc)" - make -C build test - cmake --install build + cmake --build build-release -j "$(nproc)" + make -C build-release test + cmake --install build-release mkdir pip-wheel python -m venv .venv source .venv/bin/activate @@ -68,18 +68,29 @@ jobs: - name: Build Debug run: | - cmake -B build -S . \ + cmake -B build-debug -S . \ -DCMAKE_C_COMPILER="${CC}" \ -DCMAKE_CXX_COMPILER="${CXX}" \ -DCMAKE_BUILD_TYPE=Debug \ -DBUILD_TESTS=ON \ -DBUILD_JSON_SERIALIZATION=ON \ -DBUILD_LEGACY_API=OFF \ + -DENABLE_COVERAGE=${{ matrix.compiler == 'gcc' && 'ON' || 'OFF' }} \ -DCMAKE_INSTALL_PREFIX=install-debug - cmake --build build -j "$(nproc)" - make -C build test - cmake --install build + cmake --build build-debug -j "$(nproc)" + if [ "${{ matrix.compiler }}" = "gcc" ]; then + cmake --build build-debug --target coverage + fi + cmake --install build-debug + + - name: Upload Coverage to Codecov + if: matrix.compiler == 'gcc' + uses: codecov/codecov-action@v4 + with: + file: build-debug/coverage_filtered.info + fail_ci_if_error: false + token: ${{ secrets.CODECOV_TOKEN }} build-macos: name: Build on macOS (${{ matrix.compiler }}) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6afea221..d7225aa0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,11 +50,23 @@ option(BUILD_OPENCV "Use OpenCV if found on the host system" OFF) option(BUILD_PYTHON_BINDINGS "Build Python bindings with pybind11." OFF) option(BUILD_JSON_SERIALIZATION "Build json type serialization with nlohmann json." OFF) option(ENABLE_ASAN "Build with AddressSanitizer instrumentation." OFF) +option(ENABLE_COVERAGE "Build with code coverage instrumentation." OFF) if (BUILD_TESTS) enable_testing() endif() +if (ENABLE_COVERAGE) + if (MSVC) + message(FATAL_ERROR "ENABLE_COVERAGE is only supported with Clang or GCC toolchains.") + endif() + + add_compile_options(--coverage -O0 -g) + add_link_options(--coverage) + + include(cmake/coverage.cmake) +endif() + # # We want to build "Release" by default # diff --git a/README.md b/README.md index 859930cc..b2b23946 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # LibMultiSense +[![codecov](https://codecov.io/gh/carnegierobotics/LibMultiSense/branch/master/graph/badge.svg)](https://codecov.io/gh/carnegierobotics/LibMultiSense) + LibMultiSense is a C++ and Python library designed to simplify interaction with the MultiSense S family of stereo sensors developed by Carnegie Robotics. It provides a comprehensive, easy-to-use API for capturing and processing stereo sensor data an generating depth images, color images, and 3D point clouds. @@ -197,6 +199,21 @@ the following CMake argument should be set This will require a system installation of googletest, or an installation which can be pointed to with CMake's `CMAKE_PREFIX_PATH` argument +### Code Coverage + +LibMultiSense supports generating unit test coverage reports using `lcov` and `genhtml`. +To enable coverage instrumentation, set the following CMake argument: + + -DENABLE_COVERAGE=ON + +Once enabled, you can generate the coverage report by running: + +```bash +make coverage +``` + +The report will be generated in `build/coverage_report/index.html`. This requires `lcov` and `genhtml` to be installed on the system and is supported on Linux with GCC or Clang. + --- ## Installation diff --git a/cmake/coverage.cmake b/cmake/coverage.cmake new file mode 100644 index 00000000..0bfc72e8 --- /dev/null +++ b/cmake/coverage.cmake @@ -0,0 +1,43 @@ +# +# Coverage reporting +# + +find_program(LCOV_PATH lcov) +find_program(GENHTML_PATH genhtml) + +if(NOT LCOV_PATH) + message(FATAL_ERROR "lcov not found! Coverage target will not be available.") +endif() + +if(NOT GENHTML_PATH) + message(FATAL_ERROR "genhtml not found! Coverage target will not be available.") +endif() + +# Detect lcov version to use compatible flags +execute_process(COMMAND ${LCOV_PATH} --version OUTPUT_VARIABLE LCOV_VERSION_OUTPUT) +if(LCOV_VERSION_OUTPUT MATCHES "version ([2-9][0-9]*\\.[0-9]+)") + message(STATUS "Detected lcov version 2.0+ (${CMAKE_MATCH_1})") + # For lcov 2.0+, we need to ignore certain errors that were previously warnings + set(LCOV_FLAGS --ignore-errors mismatch,unused) +else() + message(STATUS "Detected lcov version 1.x") + # For lcov 1.x, we use older ignore-errors categories + set(LCOV_FLAGS --ignore-errors gcov,source) +endif() + +add_custom_target(coverage + # Cleanup old data + COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --zerocounters + # Run tests + COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure + # Capture coverage data + COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --capture --output-file ${CMAKE_BINARY_DIR}/coverage.info ${LCOV_FLAGS} + # Remove system headers and third-party code + COMMAND ${LCOV_PATH} --remove ${CMAKE_BINARY_DIR}/coverage.info '/usr/*' '*/test/*' '*/vcpkg_installed/*' '*/details/legacy/*' --output-file ${CMAKE_BINARY_DIR}/coverage_filtered.info ${LCOV_FLAGS} + # Generate HTML report + COMMAND ${GENHTML_PATH} ${CMAKE_BINARY_DIR}/coverage_filtered.info --output-directory ${CMAKE_BINARY_DIR}/coverage_report + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + COMMENT "Generating unit test coverage report" +) + +message(STATUS "Coverage target 'coverage' added. Run 'make coverage' to generate report.")