Michal Vasko | d77e5af | 2021-06-04 08:32:29 +0200 | [diff] [blame] | 1 | # generate test code coverage report |
| 2 | |
| 3 | # check that coverage tools are available - always use before GEN_COVERAGE |
| 4 | macro(GEN_COVERAGE_ENABLE ENABLE_TESTS) |
| 5 | # make into normal variable |
| 6 | set(TESTS_ENABLED ${ENABLE_TESTS}) |
| 7 | |
| 8 | set(GEN_COVERAGE_ENABLED ON) |
| 9 | if(NOT TESTS_ENABLED) |
Michal Vasko | cdffdab | 2021-06-23 14:00:10 +0200 | [diff] [blame] | 10 | message(WARNING "You cannot generate coverage when tests are disabled. Enable test by additing parameter -DENABLE_TESTS=ON or run cmake with Debug build target.") |
Michal Vasko | d77e5af | 2021-06-04 08:32:29 +0200 | [diff] [blame] | 11 | set(GEN_COVERAGE_ENABLED OFF) |
| 12 | endif() |
| 13 | |
| 14 | if(GEN_COVERAGE_ENABLED) |
| 15 | find_program(PATH_GCOV NAMES gcov) |
| 16 | if(NOT PATH_GCOV) |
| 17 | message(WARNING "gcov executable not found! Disabling building code coverage report.") |
| 18 | set(GEN_COVERAGE_ENABLED OFF) |
| 19 | endif() |
| 20 | endif() |
| 21 | |
| 22 | if(GEN_COVERAGE_ENABLED) |
| 23 | find_program(PATH_LCOV NAMES lcov) |
| 24 | if(NOT PATH_LCOV) |
| 25 | message(WARNING "lcov executable not found! Disabling building code coverage report.") |
| 26 | set(GEN_COVERAGE_ENABLED OFF) |
| 27 | endif() |
| 28 | endif() |
| 29 | |
| 30 | if(GEN_COVERAGE_ENABLED) |
| 31 | find_program(PATH_GENHTML NAMES genhtml) |
| 32 | if(NOT PATH_GENHTML) |
| 33 | message(WARNING "genhtml executable not found! Disabling building code coverage report.") |
| 34 | set(GEN_COVERAGE_ENABLED OFF) |
| 35 | endif() |
| 36 | endif() |
| 37 | |
| 38 | if(GEN_COVERAGE_ENABLED) |
| 39 | if(NOT CMAKE_COMPILER_IS_GNUCC) |
| 40 | message(WARNING "Compiler is not gcc! Coverage may break the tests!") |
| 41 | endif() |
| 42 | |
Michal Vasko | 8c65610 | 2021-06-04 10:24:38 +0200 | [diff] [blame] | 43 | execute_process( |
| 44 | COMMAND bash "-c" "${CMAKE_C_COMPILER} --version | head -n1 | sed \"s/.* (.*) \\([0-9]\\+.[0-9]\\+.[0-9]\\+ .*\\)/\\1/\"" |
| 45 | OUTPUT_VARIABLE GCC_VERSION_FULL |
| 46 | OUTPUT_STRIP_TRAILING_WHITESPACE |
| 47 | ) |
| 48 | execute_process( |
| 49 | COMMAND bash "-c" "${PATH_GCOV} --version | head -n1 | sed \"s/.* (.*) \\([0-9]\\+.[0-9]\\+.[0-9]\\+ .*\\)/\\1/\"" |
| 50 | OUTPUT_VARIABLE GCOV_VERSION_FULL |
| 51 | OUTPUT_STRIP_TRAILING_WHITESPACE |
| 52 | ) |
| 53 | if(NOT GCC_VERSION_FULL STREQUAL GCOV_VERSION_FULL) |
| 54 | message(WARNING "gcc and gcov versions do not match! Generating coverage may fail with errors.") |
| 55 | endif() |
| 56 | |
Michal Vasko | d77e5af | 2021-06-04 08:32:29 +0200 | [diff] [blame] | 57 | # add specific required compile flags |
| 58 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage -fprofile-arcs -ftest-coverage") |
| 59 | endif() |
| 60 | endmacro() |
| 61 | |
| 62 | # tests are always expected to be in ${CMAKE_SOURCE_DIR}/tests |
| 63 | function(GEN_COVERAGE MATCH_TEST_REGEX EXCLUDE_TEST_REGEX) |
| 64 | if(NOT GEN_COVERAGE_ENABLED) |
| 65 | return() |
| 66 | endif() |
| 67 | |
| 68 | # destination |
| 69 | set(COVERAGE_DIR "${CMAKE_BINARY_DIR}/code_coverage/") |
| 70 | set(COVERAGE_FILE_RAW "${CMAKE_BINARY_DIR}/coverage_raw.info") |
| 71 | set(COVERAGE_FILE_CLEAN "${CMAKE_BINARY_DIR}/coverage_clean.info") |
| 72 | |
| 73 | # test match/exclude |
| 74 | if(MATCH_TEST_REGEX) |
| 75 | set(MATCH_TEST_ARGS -R \"${MATCH_TEST_REGEX}\") |
| 76 | endif() |
| 77 | if(EXCLUDE_TEST_REGEX) |
| 78 | set(EXCLUDE_TEST_ARGS -E \"${EXCLUDE_TEST_REGEX}\") |
| 79 | endif() |
| 80 | |
| 81 | # coverage target |
| 82 | add_custom_target(coverage |
| 83 | COMMENT "Generating code coverage..." |
| 84 | WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" |
| 85 | # Cleanup code counters |
| 86 | COMMAND "${PATH_LCOV}" --directory . --zerocounters --quiet |
| 87 | |
| 88 | # Run tests |
| 89 | COMMAND "${CMAKE_CTEST_COMMAND}" --quiet ${MATCH_TEST_ARGS} ${EXCLUDE_TEST_ARGS} |
| 90 | |
| 91 | # Capture the counters |
| 92 | COMMAND "${PATH_LCOV}" |
| 93 | --directory . |
| 94 | --rc lcov_branch_coverage=1 |
| 95 | --rc 'lcov_excl_line=assert' |
| 96 | --capture --quiet |
| 97 | --output-file "${COVERAGE_FILE_RAW}" |
| 98 | # Remove coverage of tests, system headers, etc. |
| 99 | COMMAND "${PATH_LCOV}" |
| 100 | --remove "${COVERAGE_FILE_RAW}" '${CMAKE_SOURCE_DIR}/tests/*' |
| 101 | --rc lcov_branch_coverage=1 |
| 102 | --quiet --output-file "${COVERAGE_FILE_CLEAN}" |
| 103 | # Generate HTML report |
| 104 | COMMAND "${PATH_GENHTML}" |
| 105 | --branch-coverage --function-coverage --quiet --title "${PROJECT_NAME}" |
| 106 | --legend --show-details --output-directory "${COVERAGE_DIR}" |
| 107 | "${COVERAGE_FILE_CLEAN}" |
| 108 | # Delete the counters |
| 109 | COMMAND "${CMAKE_COMMAND}" -E remove |
| 110 | ${COVERAGE_FILE_RAW} ${COVERAGE_FILE_CLEAN} |
| 111 | ) |
| 112 | |
| 113 | add_custom_command(TARGET coverage POST_BUILD |
| 114 | WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/tests" |
| 115 | COMMENT "To see the code coverage report, open ${COVERAGE_DIR}index.html" |
| 116 | COMMAND ; |
| 117 | ) |
| 118 | endfunction() |