diff --git a/.travis.yml b/.travis.yml index 4b618e13a74..3d6fa7bacef 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,10 +10,14 @@ before_install: - export CXX=g++-4.8 - printenv - sudo cp .travis/config.hpp /usr/include/armadillo_bits/config.hpp + - sudo apt-get install ruby lcov + - sudo gem install coveralls-lcov install: - - mkdir build && cd build && cmake -DDEBUG=OFF -DPROFILE=OFF .. && make -j4 + - mkdir build && cd build && cmake -DBUILD_WITH_COVERAGE=ON .. && make -j4 script: - - travis_wait 30 ./bin/mlpack_test -p + - travis_wait 30 ./mlpack_coverage -g gcov-4.8 +after_success: + - coveralls-lcov .coverage.total notifications: email: - mlpack-git@cc.gatech.edu diff --git a/CMake/mlpack_coverage.in b/CMake/mlpack_coverage.in new file mode 100755 index 00000000000..5a1c1599c4d --- /dev/null +++ b/CMake/mlpack_coverage.in @@ -0,0 +1,70 @@ +#!/bin/bash + +test_case="" +gcov_loc="" + +while getopts ":t:g:" opt; do + case $opt in + t) + test_case=$OPTARG + echo "Finding coverage with $OPTARG tests." + ;; + g) + gcov_loc=$OPTARG + echo "Using gcov: $OPTARG" + ;; + \?) + echo "Invalid option: -$OPTARG" + exit 1 + ;; + :) + echo "Option -$OPTARG requires an argument." + exit 1 + ;; + esac +done + +# Generate initial coverage information +lcov -b . -c -i -d ./ -o .coverage.wtest.base + +# Run the tests +if [ "$test_case" = "" ]; +then ${CMAKE_BINARY_DIR}/bin/mlpack_test +else + ${CMAKE_BINARY_DIR}/bin/mlpack_test --run_test=$test_case +fi + +# Generate coverage based on executed tests +if [ "$gcov_loc" = "" ]; +then lcov -b . -c -d ./ -o .coverage.wtest.run +else + lcov -b . -c -d ./ -o .coverage.wtest.run --gcov-tool=$gcov_loc +fi + +# Merge coverage tracefiles +lcov -a .coverage.wtest.base -a .coverage.wtest.run -o .coverage.total + +# Filtering, extracting project files +lcov -e .coverage.total "${CMAKE_CURRENT_SOURCE_DIR}/src/mlpack/*" -o .coverage.total.filtered + +# Filtering, removing test-files and main.cpp +lcov -r .coverage.total.filtered "${CMAKE_CURRENT_SOURCE_DIR}/src/mlpack/*/*_main.cpp" -o .coverage.total.filtered +lcov -r .coverage.total.filtered "${CMAKE_CURRENT_SOURCE_DIR}/src/mlpack/tests/*" -o .coverage.total.filtered + +# Extra: Replace /build/ with /src/ to unify directories +cat .coverage.total.filtered > .coverage.total + +# Extra: Clear up previous data, create html folder +if [[ -d ./coverage/ ]] ; then + rm -rf ./coverage/* +else + mkdir coverage +fi + +# Step 9: Generate webpage +genhtml -o ./coverage/ .coverage.total + +# Extra: Preserve coverage file in coveragehistory folder +[[ -d ./coveragehistory/ ]] || mkdir coveragehistory +cp .coverage.total ./coveragehistory/`date +'%Y.%m.%d-coverage'` + diff --git a/CMakeLists.txt b/CMakeLists.txt index b5ce3bca7c7..4c0a3322cb0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,7 @@ option(BUILD_TESTS "Build tests." ON) option(BUILD_CLI_EXECUTABLES "Build command-line executables" ON) option(BUILD_SHARED_LIBS "Compile shared libraries (if OFF, static libraries are compiled)" ON) - +option(BUILD_WITH_COVERAGE "Build with COVTOOL" OFF) enable_testing() # Include modules in the CMake directory. @@ -74,6 +74,34 @@ if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -lm") endif() +# Setup build for test coverage +if(BUILD_WITH_COVERAGE) + # Currently coverage only works with GNU g++ + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + # Find gcov and lcov + find_program(GCOV gcov) + find_program(LCOV lcov) + + if(NOT GCOV) + message(FATAL_ERROR "gcov not found! Aborting...") + endif() + + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage -fno-inline -fno-inline-small-functions -fno-default-inline -fprofile-arcs -fkeep-inline-functions") + message(WARNING "Adding debug options for coverage") + # Remove optimizations for better line coverage + set(DEBUG ON) + + if(LCOV) + configure_file(CMake/mlpack_coverage.in mlpack_coverage) + add_custom_target(mlpack_coverage DEPENDS mlpack_test COMMAND ${PROJECT_BINARY_DIR}/mlpack_coverage) + else() + message(WARNING "'lcov' not found, local coverage report is disabled. Install 'lcov' and rerun cmake to generate local coverage report.") + endif() + else() + message(FATAL_ERROR "Coverage will only work with GNU environment.") + endif() +endif() + # Debugging CFLAGS. Turn optimizations off; turn debugging symbols on. if(DEBUG) add_definitions(-DDEBUG)