Skip to content

Commit

Permalink
Merge pull request #638 from jorisv/topic/memory_check_unit_test
Browse files Browse the repository at this point in the history
Add ADD_PYTHON_MEMORYCHECK_UNIT_TEST macro
  • Loading branch information
jorisv authored Nov 24, 2023
2 parents 0468780 + 54a8359 commit 02719f3
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 19 deletions.
32 changes: 32 additions & 0 deletions memorycheck_unit_test.cmake.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Copyright (C) 2023 LAAS-CNRS, JRL AIST-CNRS, INRIA.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
SET(PYTHON_EXECUTABLE @PYTHON_EXECUTABLE@)
SET(MEMORYCHECK_COMMAND @MEMORYCHECK_COMMAND@)
SET(PYTHON_TEST_SCRIPT @PYTHON_TEST_SCRIPT@)

execute_process(COMMAND
${MEMORYCHECK_COMMAND} -- ${PYTHON_EXECUTABLE} ${PYTHON_TEST_SCRIPT}
ERROR_VARIABLE MEMORYCHECK_OUTPUT)

# Check if there is some memory leaks
string(FIND "${MEMORYCHECK_OUTPUT}" "definitely lost: 0 bytes in 0 blocks" DEFINITELY_LOST)
string(FIND "${MEMORYCHECK_OUTPUT}" "indirectly lost: 0 bytes in 0 blocks" INDIRECTLY_LOST)

if(${DEFINITELY_LOST} GREATER -1 AND ${INDIRECTLY_LOST} GREATER -1)
message(STATUS "${PYTHON_TEST_SCRIPT} is not leaking memory")
else()
message(FATAL_ERROR "Output: ${MEMORYCHECK_OUTPUT}\n"
"${PYTHON_TEST_SCRIPT} is leaking memory\n")
endif()
81 changes: 62 additions & 19 deletions test.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -117,29 +117,15 @@ endmacro(
NAME
SOURCE)

# .rst: .. command:: ADD_PYTHON_UNIT_TEST (NAME SOURCE [MODULES...])
# .rst: .. command:: COMPUTE_PYTHONPATH (result [MODULES...])
#
# Add a test called `NAME` that runs an equivalent of ``python ${SOURCE}``,
# optionnaly with a `PYTHONPATH` set to `CMAKE_BINARY_DIR/MODULE_PATH` for each
# MODULES `SOURCE` is relative to `PROJECT_SOURCE_DIR`
# Fill `result` with all necessary environment variables (`PYTHONPATH`,
# `LD_LIBRARY_PATH`, `DYLD_LIBRARY_PATH`) to load the `MODULES` in
# `CMAKE_BINARY_DIR` (`CMAKE_BINARY_DIR/MODULE_PATH`)
#
# .. note:: :command:`FINDPYTHON` should have been called first.
#
macro(ADD_PYTHON_UNIT_TEST NAME SOURCE)
if(ENABLE_COVERAGE)
set_property(GLOBAL PROPERTY JRL_CMAKEMODULES_HAS_PYTHON_COVERAGE ON)
set(PYTHONPATH "${CMAKE_INSTALL_PREFIX}/${PYTHON_SITELIB}")
add_test(
NAME ${NAME}
COMMAND ${PYTHON_EXECUTABLE} -m coverage run --branch -p
--source=${PYTHONPATH} "${PROJECT_SOURCE_DIR}/${SOURCE}"
WORKING_DIRECTORY ${PROJECT_BINARY_DIR})
else()
add_test(NAME ${NAME} COMMAND ${PYTHON_EXECUTABLE}
"${PROJECT_SOURCE_DIR}/${SOURCE}")
set(PYTHONPATH)
endif()

function(COMPUTE_PYTHONPATH result)
set(MODULES "${ARGN}") # ARGN is not a variable
foreach(MODULE_PATH IN LISTS MODULES)
if(CMAKE_GENERATOR MATCHES "Visual Studio|Xcode")
Expand Down Expand Up @@ -172,12 +158,69 @@ macro(ADD_PYTHON_UNIT_TEST NAME SOURCE)
list(APPEND ENV_VARIABLES "LD_LIBRARY_PATH=$ENV{LD_LIBRARY_PATH}")
list(APPEND ENV_VARIABLES "DYLD_LIBRARY_PATH=$ENV{DYLD_LIBRARY_PATH}")
endif(APPLE)

set(${result}
${ENV_VARIABLES}
PARENT_SCOPE)
endfunction()

# .rst: .. command:: ADD_PYTHON_UNIT_TEST (NAME SOURCE [MODULES...])
#
# Add a test called `NAME` that runs an equivalent of ``python ${SOURCE}``,
# optionnaly with a `PYTHONPATH` set to `CMAKE_BINARY_DIR/MODULE_PATH` for each
# MODULES `SOURCE` is relative to `PROJECT_SOURCE_DIR`
#
# .. note:: :command:`FINDPYTHON` should have been called first.
#
macro(ADD_PYTHON_UNIT_TEST NAME SOURCE)
if(ENABLE_COVERAGE)
set_property(GLOBAL PROPERTY JRL_CMAKEMODULES_HAS_PYTHON_COVERAGE ON)
set(PYTHONPATH "${CMAKE_INSTALL_PREFIX}/${PYTHON_SITELIB}")
add_test(
NAME ${NAME}
COMMAND ${PYTHON_EXECUTABLE} -m coverage run --branch -p
--source=${PYTHONPATH} "${PROJECT_SOURCE_DIR}/${SOURCE}"
WORKING_DIRECTORY ${PROJECT_BINARY_DIR})
else()
add_test(NAME ${NAME} COMMAND ${PYTHON_EXECUTABLE}
"${PROJECT_SOURCE_DIR}/${SOURCE}")
set(PYTHONPATH)
endif()

set(MODULES "${ARGN}") # ARGN is not a variable
compute_pythonpath(ENV_VARIABLES ${MODULES})
set_tests_properties(${NAME} PROPERTIES ENVIRONMENT "${ENV_VARIABLES}")
endmacro(
ADD_PYTHON_UNIT_TEST
NAME
SOURCE)

# .rst: .. command:: ADD_PYTHON_MEMORYCHECK_UNIT_TEST (NAME SOURCE [MODULES...])
#
# Add a test called `NAME` that runs an equivalent of ``valgrind -- python
# ${SOURCE}``, optionnaly with a `PYTHONPATH` set to
# `CMAKE_BINARY_DIR/MODULE_PATH` for each MODULES `SOURCE` is relative to
# `PROJECT_SOURCE_DIR`
#
# .. note:: :command:`FINDPYTHON` should have been called first. .. note:: Only
# work if valgrind is installed
#
macro(ADD_PYTHON_MEMORYCHECK_UNIT_TEST NAME SOURCE)
if(MEMORYCHECK_COMMAND AND MEMORYCHECK_COMMAND MATCHES ".*valgrind$")
set(TEST_FILE_NAME memorycheck_unit_test_${NAME}.cmake)
set(PYTHON_TEST_SCRIPT "${PROJECT_SOURCE_DIR}/${SOURCE}")
configure_file(
${PROJECT_JRL_CMAKE_MODULE_DIR}/memorycheck_unit_test.cmake.in
${TEST_FILE_NAME} @ONLY)

add_test(NAME ${NAME} COMMAND ${CMAKE_COMMAND} -P ${TEST_FILE_NAME})

set(MODULES "${ARGN}") # ARGN is not a variable
compute_pythonpath(ENV_VARIABLES ${MODULES})
set_tests_properties(${NAME} PROPERTIES ENVIRONMENT "${ENV_VARIABLES}")
endif()
endmacro()

# .rst: .. command:: ADD_JULIA_UNIT_TEST (NAME SOURCE [MODULES...])
#
# Add a test called `NAME` that runs an equivalent of ``julia ${SOURCE}``.
Expand Down

0 comments on commit 02719f3

Please sign in to comment.