diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml new file mode 100644 index 00000000..79f9553e --- /dev/null +++ b/.github/workflows/coverage.yml @@ -0,0 +1,72 @@ +name: coverage + +on: + push: + branches: [ master, pull-request/* ] + pull_request: + branches: [ master, pull-request/* ] + types: [opened, synchronize, reopened] + +# Allow only one concurrent deployment to GitHub Pages +concurrency: + group: coverage-${{ github.ref }} + cancel-in-progress: true + +# Grant GITHUB_TOKEN the minimum permissions needed at the workflow level +permissions: + contents: read + +jobs: + + coverage: + name: Generate Coverage Report + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y lcov llvm gcc g++ clang cmake + + - name: Build, test, and collect coverage + run: | + cd test + ./run-coverage.sh + + - name: Upload coverage report artifact + uses: actions/upload-artifact@v4 + with: + name: coverage-report + path: test/build-coverage/coverage/ + retention-days: 30 + + - name: Upload Pages artifact + if: github.event_name == 'push' && github.ref == 'refs/heads/master' + uses: actions/upload-pages-artifact@v3 + with: + path: test/build-coverage/coverage/ + + deploy-pages: + name: Deploy to GitHub Pages + if: github.event_name == 'push' && github.ref == 'refs/heads/master' + needs: coverage + runs-on: ubuntu-22.04 + + permissions: + pages: write + id-token: write + + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 + +# GitHub Repository settings +# -> Settings -> Pages +# -> Source: gh actions diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 2bec7847..ddc22d82 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -380,6 +380,10 @@ target_compile_definitions(etl_tests PRIVATE -DETL_DEBUG) option(ETL_NO_STL "No STL" OFF) +set(EXTRA_COMPILE_OPTIONS "" CACHE STRING "Additional compiler options") +set(EXTRA_LINK_OPTIONS "" CACHE STRING "Additional linker options") +set(EXTRA_LINK_LIBS "" CACHE STRING "Additional libraries to link") + if (ETL_CXX_STANDARD MATCHES "98") message(STATUS "Compiling for C++98") set_property(TARGET etl_tests PROPERTY CXX_STANDARD 98) @@ -477,7 +481,9 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU") -Wnull-dereference -Wextra-semi -g + ${EXTRA_COMPILE_OPTIONS} ) + target_link_options(etl_tests PRIVATE ${EXTRA_LINK_OPTIONS}) endif () if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") @@ -496,7 +502,9 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") -Wextra-semi-stmt -Wc++11-extra-semi -g + ${EXTRA_COMPILE_OPTIONS} ) + target_link_options(etl_tests PRIVATE ${EXTRA_LINK_OPTIONS}) endif () if ((CMAKE_CXX_COMPILER_ID MATCHES "GNU") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) @@ -522,7 +530,7 @@ target_include_directories(etl_tests ${PROJECT_SOURCE_DIR}/../include) add_subdirectory(UnitTest++) -target_link_libraries(etl_tests PRIVATE UnitTestpp) +target_link_libraries(etl_tests PRIVATE UnitTestpp ${EXTRA_LINK_LIBS}) # Enable the 'make test' CMake target using the executable defined above add_test(etl_unit_tests etl_tests) @@ -531,4 +539,3 @@ add_test(etl_unit_tests etl_tests) # define a target that will output all of the failing or passing tests # as they appear from UnitTest++ add_custom_target(test_verbose COMMAND ${CMAKE_CTEST_COMMAND} --verbose) - diff --git a/test/run-coverage.sh b/test/run-coverage.sh new file mode 100755 index 00000000..ef977f17 --- /dev/null +++ b/test/run-coverage.sh @@ -0,0 +1,81 @@ +#!/bin/bash +# +# run-coverage.sh [gcc|clang] +# +# defaults: +# compiler: clang +# + +set -e + +export CMAKE_BUILD_PARALLEL_LEVEL=$(nproc) + +# Choose gcc or clang via cmdline +if [ "$1" = "gcc" ] ; then + COMPILER=gcc +else + # default + COMPILER=clang +fi + +if [ "$COMPILER" = "gcc" ]; then + C_COMPILER=gcc + CXX_COMPILER=g++ + GCOV="$(command -v gcov)" || { echo "gcov not found in PATH"; exit 1; } + GCOV_ADD="" + EXTRA_LINK_OPTIONS="" + EXTRA_LINK_LIBS="gcov" +elif [ "$COMPILER" = "clang" ]; then + C_COMPILER=clang + CXX_COMPILER=clang++ + GCOV="$(command -v llvm-cov)" || { echo "llvm-cov not found in PATH"; exit 1; } + GCOV_ADD="--gcov-tool gcov" + EXTRA_LINK_OPTIONS="--coverage" + EXTRA_LINK_LIBS="" +else + echo "Unsupported compiler: $COMPILER" + exit 1 +fi + +BUILD=build-coverage + +rm -rf -- "${BUILD:?}" +mkdir -p "$BUILD" +cd "$BUILD" || exit 1 +touch total.info + +for CXXSTD in 11 14 17 20 23; do + for NOSTL in OFF ON; do + rm -rf CMakeFiles + cmake -DEXTRA_COMPILE_OPTIONS="--coverage" \ + -DEXTRA_LINK_OPTIONS="$EXTRA_LINK_OPTIONS" \ + -DEXTRA_LINK_LIBS="$EXTRA_LINK_LIBS" \ + -DCMAKE_C_COMPILER=$C_COMPILER \ + -DCMAKE_CXX_COMPILER=$CXX_COMPILER \ + -DNO_STL=$NOSTL \ + -DETL_USE_TYPE_TRAITS_BUILTINS=OFF \ + -DETL_USER_DEFINED_TYPE_TRAITS=OFF \ + -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF \ + -DETL_OPTIMISATION=-O0 \ + -DETL_CXX_STANDARD=$CXXSTD \ + -DETL_ENABLE_SANITIZER=Off \ + -DETL_MESSAGES_ARE_NOT_VIRTUAL=OFF \ + -DETL_USE_BUILTIN_MEM_FUNCTIONS=ON .. + cmake --build . + ./etl_tests + lcov --gcov-tool "$GCOV" $GCOV_ADD --capture --directory CMakeFiles/etl_tests.dir \ + --rc "geninfo_unexecuted_blocks=1" --output-file coverage.info --include '*/include/etl/*' --rc "lcov_branch_coverage=1" \ + --ignore-errors inconsistent \ + --ignore-errors mismatch + + lcov -a total.info -a coverage.info -o total.info --rc "lcov_branch_coverage=1" \ + --ignore-errors inconsistent \ + --ignore-errors corrupt \ + --ignore-errors empty + done +done + +genhtml total.info --output-directory coverage --rc "genhtml_branch_coverage=1" --branch-coverage -t $COMPILER \ + --ignore-errors inconsistent + +cd ..