# # Testing CamiTK python module (defined as ${PYCAMITK_LIB_NAME}) # # Testing CamiTK python module is done in an automatically generated python environment (in directory VENV_DIR). # Tests are written in the PYCAMITK_TEST_SCRIPT. # All python packages required by the tests must be declared in TEST_REQUIREMENTS and are automatically # installed in the virtual environment. They will all be labeled PYTEST_TARGET+test-name # # This is done in 3 steps: # 1. use python to create a virtual env # 2. use PYTEST_DISCOVER_TESTS_SCRIPT to generate CTEST_GENERATED_FILE, a CMake file that contains the list of ctests to add # 3. include CTEST_GENERATED_FILE in the CMake current configuration # # PYTEST_DISCOVER_TESTS_SCRIPT read the PYCAMITK_TEST_SCRIPT using ast (abstract syntax tree) package to list all the # function that start with "test_" (that is all the functions used by pytest to generate a test). It then add one ctest # for each pytest. # # Note: # - if you modify PYCAMITK_TEST_SCRIPT or PYTEST_DISCOVER_TESTS_SCRIPT, CMake will detect it automatically and reconfigure the project # - if you modify TEST_REQUIREMENTS, CMake will detect it automatically and rerun pip install -r ${TEST_REQUIREMENTS} # # Warning: # VENV_DIR, PYTHON_EXTRA_PATH and PYTEST_TARGET variables are required by the CTEST_GENERATED_FILE (see PYTEST_DISCOVER_TESTS_SCRIPT source): # - VENV_DIR is used to determine the python to use (the venv should have pytest installed) # - PYTHON_EXTRA_PATH is used to determine were the python module to be tested is found # - PYTEST_TARGET is used to add a label in order for ctest/CI to associate the test to the given target # Required variables set(VENV_DIR "${CMAKE_CURRENT_BINARY_DIR}/.venv") set(TEST_REQUIREMENTS "${CMAKE_CURRENT_SOURCE_DIR}/requirements.txt") set(PYTEST_DISCOVER_TESTS_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/pytest_discover_tests.py") set(PYTEST_TARGET "${PYCAMITK_TARGET_LIB_NAME}") # to associate the test with the target set(PYTHON_EXTRA_PATH "${OUTPUT_DIRECTORY}") # where camik python module shared object is built set(PYCAMITK_TEST_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/camitk_python_test.py") set(CTEST_GENERATED_FILE "${CMAKE_CURRENT_BINARY_DIR}/generated_pytests.cmake") if(WIN32) set(VENV_EXECUTABLE_DIR "${VENV_DIR}/Scripts") # On Windows, add ${CAMITK_BUILD_PRIVATE_LIB_DIR} and ${CAMITK_BUILD_BIN_DIR} to PATH in order # to find the public and private library dll if (MSVC) # MSVC requires path to lib for the DLL string(REGEX REPLACE "/" "\\\\" PATH_TO_DLL "${CAMITK_BUILD_PRIVATE_LIB_DIR};${CAMITK_BUILD_BIN_DIR}") # all ; needs to be escaped as well... # see https://developercommunity.visualstudio.com/t/cmake-tests-environment-variables-ignored/210188#T-N931342 string(REPLACE ";" "\\;" ESCAPED_PATH "$ENV{PATH};${PATH_TO_DLL}") # file(TO_NATIVE_PATH "${PYTHON_EXTRA_PATH}" PYTHON_EXTRA_PATH) string(REGEX REPLACE "/" "\\\\" PYTHON_EXTRA_PATH "${PYTHON_EXTRA_PATH}") message(STATUS "On windows: PYTHON_EXTRA_PATH=${PYTHON_EXTRA_PATH}") endif() else() set(VENV_EXECUTABLE_DIR "${VENV_DIR}/bin") unset(ESCAPED_PATH) endif() # # 1. create python virtual environment for testing # if (NOT EXISTS ${VENV_DIR}) message(STATUS "Creating python virtual environment for CamiTK python module tests using python '${Python_EXECUTABLE}' in directory ${VENV_DIR}...") execute_process(COMMAND "${Python_EXECUTABLE}" "-m" "venv" "${VENV_DIR}" RESULT_VARIABLE PYTHON_CREATE_VENV_RETURN_CODE OUTPUT_VARIABLE PYTHON_CREATE_VENV_OUTPUT ERROR_VARIABLE PYTHON_CREATE_VENV_OUTPUT OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_STRIP_TRAILING_WHITESPACE) if(PYTHON_CREATE_VENV_RETURN_CODE) message(FATAL_ERROR "Failed to create python virtual environment:\nreturn code:\n${PYTHON_CREATE_VENV_RETURN_CODE}\noutput:\n${PYTHON_CREATE_VENV_OUTPUT}") else() message(STATUS "Python virtual environment for CamiTK python module tests created in ${VENV_DIR}") endif() message(STATUS "Installing CamiTK python module tests requirements...") execute_process(COMMAND "${VENV_EXECUTABLE_DIR}/pip" "install" "-r" "${TEST_REQUIREMENTS}" WORKING_DIRECTORY ${VENV_DIR} RESULT_VARIABLE PYTHON_INSTALL_REQUIREMENTS_RETURN_CODE OUTPUT_VARIABLE PYTHON_INSTALL_REQUIREMENTS_OUTPUT ERROR_VARIABLE PYTHON_INSTALL_REQUIREMENTS_OUTPUT OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_STRIP_TRAILING_WHITESPACE) if(PYTHON_INSTALL_REQUIREMENTS_RETURN_CODE) message(FATAL_ERROR "Failed to install requirements for CamiTK python module tests:\nreturn code:\n${PYTHON_INSTALL_REQUIREMENTS_RETURN_CODE}\noutput:\n${PYTHON_INSTALL_REQUIREMENTS_OUTPUT}") else() message(STATUS "Python virtual environment for CamiTK python module tests ready.") endif() endif() # reinstall dependencies if requirements.txt changes add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/requirements-last-updated.txt COMMAND "${VENV_EXECUTABLE_DIR}/pip" "install" "-r" "${TEST_REQUIREMENTS}" COMMAND ${CMAKE_COMMAND} -E echo "Pip install executed" > ${CMAKE_CURRENT_BINARY_DIR}/requirements-last-updated.txt DEPENDS ${TEST_REQUIREMENTS} COMMENT "Updating CamiTK python module test dependencies because ${TEST_REQUIREMENTS} has changed" VERBATIM ) add_custom_target( ${PYCAMITK_TARGET_LIB_NAME}-test-requirements ALL COMMENT "Update CamiTK python module test requirements when necessary" DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/requirements-last-updated.txt ) # # 2. Create the list of tests from PYCAMITK_TEST_FILE (at configure time) # execute_process(COMMAND "${VENV_EXECUTABLE_DIR}/python" ${PYTEST_DISCOVER_TESTS_SCRIPT} ${PYCAMITK_TEST_SCRIPT} "${CTEST_GENERATED_FILE}" "${PYTHON_EXTRA_PATH}" "${ESCAPED_PATH}" WORKING_DIRECTORY ${VENV_DIR} RESULT_VARIABLE PYTHON_GENERATE_TESTS_RETURN_CODE OUTPUT_VARIABLE PYTHON_GENERATE_TESTS_OUTPUT ERROR_VARIABLE PYTHON_GENERATE_TESTS_OUTPUT OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_STRIP_TRAILING_WHITESPACE) if(PYTHON_GENERATE_TESTS_RETURN_CODE) message(FATAL_ERROR "Failed to generate tests from pytest:\nreturn code:\n${PYTHON_GENERATE_TESTS_RETURN_CODE}\noutput:\n${PYTHON_GENERATE_TESTS_OUTPUT}") else() message(STATUS "Python test generated from pytest using PYTHON_EXTRA_PATH=${PYTHON_EXTRA_PATH}.") endif() set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${PYTEST_DISCOVER_TESTS_SCRIPT}" "${PYCAMITK_TEST_SCRIPT}" ) # # 3. Include the generated tests # include("${CTEST_GENERATED_FILE}")