build REFACTOR separate code coverage into a module
diff --git a/CMakeModules/GenCoverage.cmake b/CMakeModules/GenCoverage.cmake
new file mode 100644
index 0000000..43a576b
--- /dev/null
+++ b/CMakeModules/GenCoverage.cmake
@@ -0,0 +1,104 @@
+# generate test code coverage report
+
+# check that coverage tools are available - always use before GEN_COVERAGE
+macro(GEN_COVERAGE_ENABLE ENABLE_TESTS)
+ # make into normal variable
+ set(TESTS_ENABLED ${ENABLE_TESTS})
+
+ set(GEN_COVERAGE_ENABLED ON)
+ if(NOT TESTS_ENABLED)
+ message(WARNING "You cannot generate coverage when tests are disabled. Enable test by additing parameter -DENABLE_BUILD_TESTS=ON or run cmake with Debug build target.")
+ set(GEN_COVERAGE_ENABLED OFF)
+ endif()
+
+ if(GEN_COVERAGE_ENABLED)
+ find_program(PATH_GCOV NAMES gcov)
+ if(NOT PATH_GCOV)
+ message(WARNING "gcov executable not found! Disabling building code coverage report.")
+ set(GEN_COVERAGE_ENABLED OFF)
+ endif()
+ endif()
+
+ if(GEN_COVERAGE_ENABLED)
+ find_program(PATH_LCOV NAMES lcov)
+ if(NOT PATH_LCOV)
+ message(WARNING "lcov executable not found! Disabling building code coverage report.")
+ set(GEN_COVERAGE_ENABLED OFF)
+ endif()
+ endif()
+
+ if(GEN_COVERAGE_ENABLED)
+ find_program(PATH_GENHTML NAMES genhtml)
+ if(NOT PATH_GENHTML)
+ message(WARNING "genhtml executable not found! Disabling building code coverage report.")
+ set(GEN_COVERAGE_ENABLED OFF)
+ endif()
+ endif()
+
+ if(GEN_COVERAGE_ENABLED)
+ if(NOT CMAKE_COMPILER_IS_GNUCC)
+ message(WARNING "Compiler is not gcc! Coverage may break the tests!")
+ endif()
+
+ # add specific required compile flags
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage -fprofile-arcs -ftest-coverage")
+ endif()
+endmacro()
+
+# tests are always expected to be in ${CMAKE_SOURCE_DIR}/tests
+function(GEN_COVERAGE MATCH_TEST_REGEX EXCLUDE_TEST_REGEX)
+ if(NOT GEN_COVERAGE_ENABLED)
+ return()
+ endif()
+
+ # destination
+ set(COVERAGE_DIR "${CMAKE_BINARY_DIR}/code_coverage/")
+ set(COVERAGE_FILE_RAW "${CMAKE_BINARY_DIR}/coverage_raw.info")
+ set(COVERAGE_FILE_CLEAN "${CMAKE_BINARY_DIR}/coverage_clean.info")
+
+ # test match/exclude
+ if(MATCH_TEST_REGEX)
+ set(MATCH_TEST_ARGS -R \"${MATCH_TEST_REGEX}\")
+ endif()
+ if(EXCLUDE_TEST_REGEX)
+ set(EXCLUDE_TEST_ARGS -E \"${EXCLUDE_TEST_REGEX}\")
+ endif()
+
+ # coverage target
+ add_custom_target(coverage
+ COMMENT "Generating code coverage..."
+ WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
+ # Cleanup code counters
+ COMMAND "${PATH_LCOV}" --directory . --zerocounters --quiet
+
+ # Run tests
+ COMMAND "${CMAKE_CTEST_COMMAND}" --quiet ${MATCH_TEST_ARGS} ${EXCLUDE_TEST_ARGS}
+
+ # Capture the counters
+ COMMAND "${PATH_LCOV}"
+ --directory .
+ --rc lcov_branch_coverage=1
+ --rc 'lcov_excl_line=assert'
+ --capture --quiet
+ --output-file "${COVERAGE_FILE_RAW}"
+ # Remove coverage of tests, system headers, etc.
+ COMMAND "${PATH_LCOV}"
+ --remove "${COVERAGE_FILE_RAW}" '${CMAKE_SOURCE_DIR}/tests/*'
+ --rc lcov_branch_coverage=1
+ --quiet --output-file "${COVERAGE_FILE_CLEAN}"
+ # Generate HTML report
+ COMMAND "${PATH_GENHTML}"
+ --branch-coverage --function-coverage --quiet --title "${PROJECT_NAME}"
+ --legend --show-details --output-directory "${COVERAGE_DIR}"
+ "${COVERAGE_FILE_CLEAN}"
+ # Delete the counters
+ COMMAND "${CMAKE_COMMAND}" -E remove
+ ${COVERAGE_FILE_RAW} ${COVERAGE_FILE_CLEAN}
+ )
+
+ add_custom_command(TARGET coverage POST_BUILD
+ WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/tests"
+ COMMENT "To see the code coverage report, open ${COVERAGE_DIR}index.html"
+ COMMAND ;
+ )
+endfunction()