Skip to content

Instantly share code, notes, and snippets.

@dnmiller
Last active April 21, 2019 10:08
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save dnmiller/6348584 to your computer and use it in GitHub Desktop.
Save dnmiller/6348584 to your computer and use it in GitHub Desktop.
CMake custom target for adding Python tests and GoogleTest unit tests to compiled libraries. Tests are run via nose.
# Custom cmake target for creating a target using gtest for unit testing.
function(add_gtest_target TARGET_NAME TARGET_LIB)
set(TEST_EXEC _${TARGET_LIB}_test)
# Build the test executable.
add_executable(${TEST_EXEC} ${ARGN})
target_link_libraries(${TEST_EXEC} ${TARGET_LIB})
# gtest was not found with a find_package command and must be linked to
# explicitly.
target_link_libraries(${TEST_EXEC} gtest_main)
# This adds the "make test" target.
add_custom_target(${TARGET_NAME}
COMMAND ${TEST_EXEC}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Running tests.")
endfunction()
function(add_gtest_coverage_target TARGET_NAME OUTPUT_NAME TEST_SOURCES)
# Check prereqs
find_program(LCOV_PATH lcov)
find_program(GENHTML_PATH genhtml)
if (NOT LCOV_PATH)
message(FATAL_ERROR "lcov not found! Aborting...")
endif()
if (NOT GENHTML_PATH)
message(FATAL_ERROR "genhtml not found! Aborting...")
endif()
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
message(WARNING "Code coverage results with a non-Debug build may be misleading" )
endif()
set(EXEC_NAME _${TARGET_NAME}_coverage)
add_executable(${EXEC_NAME} ${TEST_SOURCES})
# Link to the coverage libraries.
target_link_libraries(${EXEC_NAME} profile_rt)
# Link to google test libraries.
target_link_libraries(${EXEC_NAME} gtest_main)
# Add flags for debugging and for code coverage tests.
set_target_properties(${EXEC_NAME} PROPERTIES COMPILE_FLAGS
"-g -Wall -Wextra -fprofile-arcs -ftest-coverage")
set_target_properties(${EXEC_NAME} PROPERTIES LINKER_FLAGS
"-fprofile-arcs -ftest-coverage -lgcov")
# Create...
add_custom_target(${TARGET_NAME}
COMMAND ${LCOV_PATH} --directory . --zerocounters
COMMAND ${EXEC_NAME}
# Capture lcov counters
COMMAND ${LCOV_PATH}
--directory . --capture --output-file ${OUTPUT_NAME}.info
# Remove googletest coverage results
COMMAND ${LCOV_PATH}
--remove ${OUTPUT_NAME}.info '/usr/*' 'gtest*' 'include/*'
--output-file ${OUTPUT_NAME}.info.cleaned
COMMAND ${GENHTML_PATH}
-o ${OUTPUT_NAME} ${OUTPUT_NAME}.info.cleaned --legend -s
COMMAND ${CMAKE_COMMAND}
-E remove ${OUTPUT_NAME}.info ${OUTPUT_NAME}.info.cleaned
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Generating code coverage results.")
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
COMMENT "Open ./test/${OUTPUT_NAME}/index.html in your browser to
view the coverage report.")
endfunction()
# Custom cmake target for running nosetests on compiled libraries.
#
# This creates a custom target for running nosetests with the intent of
# verifying a compiled library using interpreted Python unit tests. It is
# meant to be used in addition to, not in lieu of, compiled unit test code.
# This can be particularly useful for math-heavy code where a compiled unit
# test could be quite cumbersome. The Python test code is responsible for
# importing the libraries (probably using ctypes).
#
# Usage:
#
# add_python_test_target(TARGET_NAME LIBRARY_DEPENDENCY SOURCE_FILES)
#
# Released into the public domain. No warranty implied.
find_program(NOSETESTS_PATH nosetests)
if(NOT NOSETESTS_PATH)
message(WARNING
"nosetests not found! Python library tests will not be available.")
endif()
function(add_python_test_target TARGET_NAME TARGET_LIB)
# Try again to find nosetests here. We may have switched virtualenvs or
# something since first running cmake.
find_program(NOSETESTS_PATH nosetests)
if(NOT NOSETESTS_PATH)
message(FATAL_ERROR "nosetests not found! Aborting...")
endif()
set(COPY_DIR ${CMAKE_BINARY_DIR}/${TARGET_NAME}_files)
add_custom_target(${TARGET_NAME}
COMMAND ${NOSETESTS_PATH}
WORKING_DIRECTORY ${COPY_DIR}
COMMENT "Running Python tests.")
# Copy Python files to the local binary directory so they can find the
# dynamic library.
set(COPY_TARGET ${TARGET_NAME}_copy)
# We add a separate target for copying Python files. This way we can set
# it as a dependency for the nosetests target.
add_custom_target(${COPY_TARGET})
# Make sure the directory exists before we copy the files.
add_custom_command(TARGET ${COPY_TARGET}
COMMAND ${CMAKE_COMMAND} -E make_directory ${COPY_DIR})
# Add commands to copy each file in the list of sources.
foreach(pysource ${ARGN})
add_custom_command(TARGET ${COPY_TARGET}
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_SOURCE_DIR}/${pysource} ${COPY_DIR})
endforeach()
get_target_property(TARGET_LIB_NAME ${TARGET_LIB} LOCATION)
# Add a command to copy the target library into the same folder as the
# python files.
add_custom_command(TARGET ${COPY_TARGET}
COMMAND ${CMAKE_COMMAND} -E copy ${TARGET_LIB_NAME} ${COPY_DIR}
)
# Make the copy target a dependency of the testing target to ensure it
# gets done first.
add_dependencies(${TARGET_NAME} ${COPY_TARGET})
add_dependencies(${COPY_TARGET} ${TARGET_LIB})
endfunction()
@bschwb
Copy link

bschwb commented Mar 30, 2016

PythonLibTest.cmake: Line 57 produces a warning since cmake-3.5.1
One can use $<TARGET_FILE:${TARGET_LIB}> instead of Line 57 and ${TARGET_LIB_NAME} in Line 62.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment