Pretty bare bones but gets the job done, unlike the gcc tooling, this will work for release builds, the performance cost of it is on the high side of things, the full test suite tests take over an hour for me with code coverage support enabled on a release build. I have not timed a debug build. Given developers can just run their tests to get coverage data over what they are working on, I feel this is still useful tooling to have. This adds the 3 targets for clang and adds a single gcc target coverage-reset - this removes the collected code coverage data and report coverage-report - This merges the collected data and generates the report (new for gcc) coverage-show - This merges the collected data and generates the report and opens it in the browser This relies on llvm-cov and llvm-profdata being available if not found code coverage is disabled. Note: A full test run requires an obscene amount of disk space, a complete test run takes about 125GB and takes 12 minutes to merge, so provision the COMPILER_CODE_COVERAGE_DATA_DIR folder accordingly Example report in PR
423 lines
14 KiB
CMake
423 lines
14 KiB
CMake
# SPDX-FileCopyrightText: 2006-2023 Blender Authors
|
|
#
|
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
function(get_blender_test_install_dir VARIABLE_NAME)
|
|
get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
|
if(GENERATOR_IS_MULTI_CONFIG)
|
|
string(REPLACE "\${BUILD_TYPE}" "$<CONFIG>" TEST_INSTALL_DIR ${CMAKE_INSTALL_PREFIX})
|
|
else()
|
|
string(REPLACE "\${BUILD_TYPE}" "" TEST_INSTALL_DIR ${CMAKE_INSTALL_PREFIX})
|
|
endif()
|
|
set(${VARIABLE_NAME} "${TEST_INSTALL_DIR}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
# Add the necessary LSAN/ASAN options to the given list of environment variables.
|
|
# Typically used after adding a test, before calling
|
|
# `set_tests_properties(${testname} PROPERTIES ENVIRONMENT "${_envvar_list}")`,
|
|
# to ensure that it will run with the correct sanitizer settings.
|
|
#
|
|
# \param envvars_list: A list of extra environment variables to define for that test.
|
|
# Note that this does no check for (re-)definition of a same variable.
|
|
function(blender_test_set_envvars testname envvar_list)
|
|
if(PLATFORM_ENV_INSTALL)
|
|
list(APPEND envvar_list "${PLATFORM_ENV_INSTALL}")
|
|
endif()
|
|
|
|
if(NOT CMAKE_BUILD_TYPE MATCHES "Release")
|
|
if(WITH_COMPILER_ASAN)
|
|
set(_lsan_options "LSAN_OPTIONS=print_suppressions=false:suppressions=${CMAKE_SOURCE_DIR}/tools/config/analysis/lsan.supp")
|
|
# FIXME: That `allocator_may_return_null=true` ASAN option is only needed for the
|
|
# `guardedalloc` test, would be nice to allow tests definition to pass extra envvars better.
|
|
set(_asan_options "ASAN_OPTIONS=allocator_may_return_null=true")
|
|
if(DEFINED ENV{LSAN_OPTIONS})
|
|
set(_lsan_options "${_lsan_options}:$ENV{LSAN_OPTIONS}")
|
|
endif()
|
|
if(DEFINED ENV{ASAN_OPTIONS})
|
|
set(_asan_options "${_asan_options}:$ENV{ASAN_OPTIONS}")
|
|
endif()
|
|
list(APPEND envvar_list "${_lsan_options}" "${_asan_options}")
|
|
endif()
|
|
endif()
|
|
if(WITH_COMPILER_CODE_COVERAGE AND CMAKE_C_COMPILER_ID MATCHES "Clang")
|
|
list(APPEND envvar_list "LLVM_PROFILE_FILE=${COMPILER_CODE_COVERAGE_DATA_DIR}/raw/blender_%p.profraw")
|
|
endif()
|
|
# Can only be called once per test to define its custom environment variables.
|
|
set_tests_properties(${testname} PROPERTIES ENVIRONMENT "${envvar_list}")
|
|
endfunction()
|
|
|
|
macro(blender_src_gtest_ex)
|
|
if(WITH_GTESTS)
|
|
set(options)
|
|
set(oneValueArgs NAME)
|
|
set(multiValueArgs SRC EXTRA_LIBS COMMAND_ARGS)
|
|
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
|
unset(options)
|
|
unset(oneValueArgs)
|
|
unset(multiValueArgs)
|
|
|
|
set(TARGET_NAME ${ARG_NAME}_test)
|
|
get_property(_current_include_directories
|
|
DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
|
PROPERTY INCLUDE_DIRECTORIES)
|
|
set(TEST_INC
|
|
${_current_include_directories}
|
|
${CMAKE_SOURCE_DIR}/tests/gtests
|
|
)
|
|
set(TEST_INC_SYS
|
|
${GLOG_INCLUDE_DIRS}
|
|
${GFLAGS_INCLUDE_DIRS}
|
|
${CMAKE_SOURCE_DIR}/extern/gtest/include
|
|
${CMAKE_SOURCE_DIR}/extern/gmock/include
|
|
)
|
|
unset(_current_include_directories)
|
|
if(WIN32 AND NOT WITH_WINDOWS_EXTERNAL_MANIFEST)
|
|
set(MANIFEST "${CMAKE_BINARY_DIR}/tests.exe.manifest")
|
|
else()
|
|
set(MANIFEST "")
|
|
endif()
|
|
|
|
add_executable(${TARGET_NAME} ${ARG_SRC} ${MANIFEST})
|
|
setup_platform_linker_flags(${TARGET_NAME})
|
|
target_compile_definitions(${TARGET_NAME} PRIVATE ${GFLAGS_DEFINES})
|
|
target_compile_definitions(${TARGET_NAME} PRIVATE ${GLOG_DEFINES})
|
|
target_include_directories(${TARGET_NAME} PUBLIC "${TEST_INC}")
|
|
target_include_directories(${TARGET_NAME} SYSTEM PUBLIC "${TEST_INC_SYS}")
|
|
blender_link_libraries(${TARGET_NAME} "${ARG_EXTRA_LIBS};${PLATFORM_LINKLIBS}")
|
|
if(WITH_TBB)
|
|
# Force TBB libraries to be in front of MKL (part of OpenImageDenoise), so
|
|
# that it is initialized before MKL and static library initialization order
|
|
# issues are avoided.
|
|
target_link_libraries(${TARGET_NAME} PRIVATE ${TBB_LIBRARIES})
|
|
if(WITH_OPENIMAGEDENOISE)
|
|
target_link_libraries(${TARGET_NAME} PRIVATE ${OPENIMAGEDENOISE_LIBRARIES})
|
|
endif()
|
|
endif()
|
|
target_link_libraries(${TARGET_NAME} PRIVATE
|
|
bf_testing_main
|
|
bf_intern_eigen
|
|
bf_intern_guardedalloc
|
|
extern_gtest
|
|
extern_gmock
|
|
# Needed for GLOG.
|
|
${GLOG_LIBRARIES}
|
|
${GFLAGS_LIBRARIES})
|
|
|
|
if(DEFINED PTHREADS_LIBRARIES) # Needed for GLOG.
|
|
target_link_libraries(${TARGET_NAME} PRIVATE ${PTHREADS_LIBRARIES})
|
|
endif()
|
|
if(UNIX AND NOT APPLE)
|
|
target_link_libraries(${TARGET_NAME} PRIVATE bf_intern_libc_compat)
|
|
endif()
|
|
if(WITH_TBB)
|
|
target_link_libraries(${TARGET_NAME} PRIVATE ${TBB_LIBRARIES})
|
|
endif()
|
|
if(WITH_GMP)
|
|
target_link_libraries(${TARGET_NAME} PRIVATE ${GMP_LIBRARIES})
|
|
endif()
|
|
|
|
get_blender_test_install_dir(TEST_INSTALL_DIR)
|
|
set_target_properties(${TARGET_NAME} PROPERTIES
|
|
RUNTIME_OUTPUT_DIRECTORY "${TESTS_OUTPUT_DIR}"
|
|
RUNTIME_OUTPUT_DIRECTORY_RELEASE "${TESTS_OUTPUT_DIR}"
|
|
RUNTIME_OUTPUT_DIRECTORY_DEBUG "${TESTS_OUTPUT_DIR}")
|
|
if(WIN32)
|
|
set_target_properties(${TARGET_NAME} PROPERTIES VS_GLOBAL_VcpkgEnabled "false")
|
|
|
|
if(WITH_WINDOWS_EXTERNAL_MANIFEST)
|
|
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
|
|
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/tests.exe.manifest ${TESTS_OUTPUT_DIR}/${TARGET_NAME}.exe.manifest
|
|
)
|
|
endif()
|
|
endif()
|
|
unset(MANIFEST)
|
|
unset(TEST_INC)
|
|
unset(TEST_INC_SYS)
|
|
unset(TARGET_NAME)
|
|
endif()
|
|
endmacro()
|
|
|
|
function(blender_add_ctests)
|
|
if(ARGC LESS 1)
|
|
message(FATAL_ERROR "No arguments supplied to blender_add_ctests()")
|
|
endif()
|
|
if(NOT EXISTS "${CMAKE_SOURCE_DIR}/tests/files/render")
|
|
return()
|
|
endif()
|
|
|
|
# Parse the arguments
|
|
set(oneValueArgs DISCOVER_TESTS TARGET SUITE_NAME)
|
|
set(multiValueArgs SOURCES)
|
|
cmake_parse_arguments(ARGS "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
|
|
|
# Figure out the release dir, as some tests need files from there.
|
|
get_blender_test_install_dir(TEST_INSTALL_DIR)
|
|
if(APPLE)
|
|
set(_test_release_dir ${TEST_INSTALL_DIR}/Blender.app/Contents/Resources/${BLENDER_VERSION})
|
|
else()
|
|
if(WIN32 OR WITH_INSTALL_PORTABLE)
|
|
set(_test_release_dir ${TEST_INSTALL_DIR}/${BLENDER_VERSION})
|
|
else()
|
|
set(_test_release_dir ${TEST_INSTALL_DIR}/share/blender/${BLENDER_VERSION})
|
|
endif()
|
|
endif()
|
|
|
|
# Define a test case with our custom gtest_add_tests() command.
|
|
if(${ARGS_DISCOVER_TESTS})
|
|
include(GTest)
|
|
gtest_add_tests(
|
|
TARGET ${ARGS_TARGET}
|
|
SOURCES "${ARGS_SOURCES}"
|
|
TEST_PREFIX ${ARGS_SUITE_NAME}
|
|
WORKING_DIRECTORY "${TEST_INSTALL_DIR}"
|
|
EXTRA_ARGS
|
|
--test-assets-dir "${CMAKE_SOURCE_DIR}/tests/files"
|
|
--test-release-dir "${_test_release_dir}"
|
|
)
|
|
else()
|
|
add_test(
|
|
NAME ${ARGS_SUITE_NAME}
|
|
COMMAND ${ARGS_TARGET}
|
|
--test-assets-dir "${CMAKE_SOURCE_DIR}/tests/files"
|
|
--test-release-dir "${_test_release_dir}"
|
|
WORKING_DIRECTORY ${TEST_INSTALL_DIR}
|
|
)
|
|
endif()
|
|
blender_test_set_envvars("${ARGS_SUITE_NAME}" "")
|
|
|
|
unset(_test_release_dir)
|
|
endfunction()
|
|
|
|
# Add tests for a Blender library, to be called in tandem with blender_add_lib().
|
|
#
|
|
# If WITH_TESTS_SINGLE_BINARY is enabled, tests will be put into the blender_test
|
|
# executable, and a separate ctest will be generated for every gtest contained in it.
|
|
#
|
|
# If WITH_TESTS_SINGLE_BINARY is disabled, this works identically to
|
|
# blender_add_test_suite_executable.
|
|
#
|
|
# The function accepts an optional argument which denotes list of sources which
|
|
# is to be compiled-in with the suite sources for each fo the suites when the
|
|
# WITH_TESTS_SINGLE_BINARY configuration is set to OFF.
|
|
function(blender_add_test_suite_lib
|
|
name
|
|
sources
|
|
includes
|
|
includes_sys
|
|
library_deps
|
|
)
|
|
|
|
# Sources which are common for all suits and do not need to yield their own
|
|
# test suite binaries when WITH_TESTS_SINGLE_BINARY is OFF.
|
|
set(common_sources ${ARGN})
|
|
|
|
if(WITH_TESTS_SINGLE_BINARY)
|
|
add_cc_flags_custom_test(${name}_tests PARENT_SCOPE)
|
|
|
|
# Otherwise external projects will produce warnings that we cannot fix.
|
|
remove_strict_flags()
|
|
|
|
# This duplicates logic that's also in blender_src_gtest_ex.
|
|
# TODO(Sybren): deduplicate after the general approach in D7649 has been approved.
|
|
list(APPEND includes
|
|
${CMAKE_SOURCE_DIR}/tests/gtests
|
|
)
|
|
list(APPEND includes_sys
|
|
${GLOG_INCLUDE_DIRS}
|
|
${GFLAGS_INCLUDE_DIRS}
|
|
${CMAKE_SOURCE_DIR}/extern/gtest/include
|
|
${CMAKE_SOURCE_DIR}/extern/gmock/include
|
|
)
|
|
|
|
blender_add_lib__impl(${name}_tests
|
|
"${sources};${common_sources}" "${includes}" "${includes_sys}" "${library_deps}")
|
|
|
|
target_compile_definitions(${name}_tests PRIVATE ${GFLAGS_DEFINES})
|
|
target_compile_definitions(${name}_tests PRIVATE ${GLOG_DEFINES})
|
|
|
|
set_property(GLOBAL APPEND PROPERTY BLENDER_TEST_LIBS ${name}_tests)
|
|
|
|
blender_add_ctests(
|
|
TARGET blender_test
|
|
SUITE_NAME ${name}
|
|
SOURCES "${sources};${common_sources}"
|
|
DISCOVER_TESTS TRUE
|
|
)
|
|
else()
|
|
blender_add_test_suite_executable(
|
|
"${name}"
|
|
"${sources}"
|
|
"${includes}"
|
|
"${includes_sys}"
|
|
"${library_deps}"
|
|
"${common_sources}"
|
|
)
|
|
endif()
|
|
endfunction()
|
|
|
|
|
|
function(blender_add_test_executable_impl
|
|
name
|
|
sources
|
|
includes
|
|
includes_sys
|
|
library_deps
|
|
)
|
|
|
|
set(oneValueArgs ADD_CTESTS DISCOVER_TESTS)
|
|
set(multiValueArgs)
|
|
cmake_parse_arguments(ARGS "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
|
unset(oneValueArgs)
|
|
unset(multiValueArgs)
|
|
|
|
add_cc_flags_custom_test(${name} PARENT_SCOPE)
|
|
|
|
## Otherwise external projects will produce warnings that we cannot fix.
|
|
remove_strict_flags()
|
|
|
|
blender_src_gtest_ex(
|
|
NAME ${name}
|
|
SRC "${sources}"
|
|
EXTRA_LIBS "${library_deps}"
|
|
)
|
|
|
|
if(ARGS_ADD_CTESTS)
|
|
blender_add_ctests(
|
|
TARGET ${name}_test
|
|
SUITE_NAME ${name}
|
|
SOURCES "${sources}"
|
|
DISCOVER_TESTS ${ARGS_DISCOVER_TESTS}
|
|
)
|
|
endif()
|
|
|
|
blender_target_include_dirs(${name}_test ${includes})
|
|
blender_target_include_dirs_sys(${name}_test ${includes_sys})
|
|
blender_source_group("${name}_test" "${sources}")
|
|
endfunction()
|
|
|
|
# Add tests for a Blender library, to be called in tandem with blender_add_lib().
|
|
#
|
|
# If WITH_TESTS_SINGLE_BINARY is enabled, this will generate a single executable
|
|
# named ${name}_test, and generate a separate ctest for every gtest contained in it.
|
|
#
|
|
# If WITH_TESTS_SINGLE_BINARY is disabled, this will generate an executable
|
|
# named ${name}_${source}_test for every source file (with redundant prefixes and
|
|
# postfixes stripped).
|
|
#
|
|
# To be used for smaller isolated libraries, that do not have many dependencies.
|
|
# For libraries that do drag in many other Blender libraries and would create a
|
|
# very large executable, blender_add_test_suite_lib() should be used instead.
|
|
#
|
|
# The function accepts an optional argument which denotes list of sources which
|
|
# is to be compiled-in with the suit sources for each fo the suites when the
|
|
# WITH_TESTS_SINGLE_BINARY configuration is set to OFF.
|
|
function(blender_add_test_suite_executable
|
|
name
|
|
sources
|
|
includes
|
|
includes_sys
|
|
library_deps
|
|
)
|
|
|
|
# Sources which are common for all suits and do not need to yield their own
|
|
# test suit binaries when WITH_TESTS_SINGLE_BINARY is OFF.
|
|
set(common_sources ${ARGN})
|
|
|
|
if(WITH_TESTS_SINGLE_BINARY)
|
|
blender_add_test_executable_impl(
|
|
"${name}"
|
|
"${sources};${common_sources}"
|
|
"${includes}"
|
|
"${includes_sys}"
|
|
"${library_deps}"
|
|
ADD_CTESTS TRUE
|
|
DISCOVER_TESTS TRUE
|
|
)
|
|
else()
|
|
foreach(source ${sources})
|
|
get_filename_component(_source_ext ${source} LAST_EXT)
|
|
if(NOT ${_source_ext} MATCHES "^\.h")
|
|
# Generate test name without redundant prefixes and postfixes.
|
|
get_filename_component(_test_name ${source} NAME_WE)
|
|
if(NOT ${_test_name} MATCHES "^${name}_")
|
|
set(_test_name "${name}_${_test_name}")
|
|
endif()
|
|
string(REGEX REPLACE "_test$" "" _test_name ${_test_name})
|
|
string(REGEX REPLACE "_tests$" "" _test_name ${_test_name})
|
|
|
|
blender_add_test_executable_impl(
|
|
"${_test_name}"
|
|
"${source};${common_sources}"
|
|
"${includes}"
|
|
"${includes_sys}"
|
|
"${library_deps}"
|
|
ADD_CTESTS TRUE
|
|
DISCOVER_TESTS FALSE
|
|
)
|
|
|
|
# Work-around run-time dynamic loader error
|
|
# symbol not found in flat namespace '_PyBaseObject_Type'
|
|
#
|
|
# Some tests are testing modules which are linked against Python, while some of unit
|
|
# tests might not use code path which uses Python functionality. In this case linker
|
|
# will optimize out all symbols from Python since it decides they are not used. This
|
|
# somehow conflicts with other libraries which are linked against the test binary and
|
|
# perform search of _PyBaseObject_Type on startup.
|
|
#
|
|
# Work-around by telling the linker that the python libraries should not be stripped.
|
|
if(APPLE)
|
|
target_link_libraries("${_test_name}_test" PRIVATE "-Wl,-force_load,${PYTHON_LIBRARIES}")
|
|
endif()
|
|
|
|
if(WITH_BUILDINFO)
|
|
target_link_libraries("${_test_name}_test" PRIVATE buildinfoobj)
|
|
endif()
|
|
endif()
|
|
endforeach()
|
|
endif()
|
|
endfunction()
|
|
|
|
# Add test for a Blender library, to be called in tandem with blender_add_lib().
|
|
# Source files will be compiled into a single ${name}_test executable.
|
|
#
|
|
# To be used for smaller isolated libraries, that do not have many dependencies.
|
|
# For libraries that do drag in many other Blender libraries and would create a
|
|
# very large executable, blender_add_test_lib() should be used instead.
|
|
function(blender_add_test_executable
|
|
name
|
|
sources
|
|
includes
|
|
includes_sys
|
|
library_deps
|
|
)
|
|
blender_add_test_executable_impl(
|
|
"${name}"
|
|
"${sources}"
|
|
"${includes}"
|
|
"${includes_sys}"
|
|
"${library_deps}"
|
|
ADD_CTESTS TRUE
|
|
DISCOVER_TESTS FALSE
|
|
)
|
|
endfunction()
|
|
|
|
# Add performance test. This is like blender_add_test_executable, but no ctest
|
|
# is generated and the binary should be run manually.
|
|
function(blender_add_test_performance_executable
|
|
name
|
|
sources
|
|
includes
|
|
includes_sys
|
|
library_deps
|
|
)
|
|
blender_add_test_executable_impl(
|
|
"${name}"
|
|
"${sources}"
|
|
"${includes}"
|
|
"${includes_sys}"
|
|
"${library_deps}"
|
|
ADD_CTESTS FALSE
|
|
DISCOVER_TESTS FALSE
|
|
)
|
|
endfunction()
|