diff --git a/cmake/modules/Sanitizers.cmake b/cmake/modules/Sanitizers.cmake index a5f1cc21df..1e7351502e 100644 --- a/cmake/modules/Sanitizers.cmake +++ b/cmake/modules/Sanitizers.cmake @@ -1,39 +1,52 @@ # Check if the requested sanitizers are supported, and set the appropriated # flags. function(enable_sanitizers) # Build the -fsanitize= option list(JOIN ARGN "," _fsanitize_option_list) set(_fsanitize_option -fsanitize=${_fsanitize_option_list}) include(SanitizeHelper) sanitize_c_cxx_definition("supports_" ${_fsanitize_option} _sanitizers_compile) # check_cxx_source_compiles() runs the compilation and link phases at the # same time, so add the flag under test for both. set(CMAKE_REQUIRED_FLAGS ${_fsanitize_option}) set(_save_linker_flags ${CMAKE_EXE_LINKER_FLAGS}) string(APPEND CMAKE_EXE_LINKER_FLAGS " ${_fsanitize_option}") include(CheckCXXSourceCompiles) check_cxx_source_compiles(" #include #include extern \"C\" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { return 0; } __attribute__((weak)) int main() { return 0; } " ${_sanitizers_compile}) set(CMAKE_EXE_LINKER_FLAGS ${_save_linker_flags}) if(NOT ${_sanitizers_compile}) message(FATAL_ERROR "The sanitizers option is not supported: ${_fsanitize_option}") endif() # Some sanitizers require some extra options to be efficient if("address" IN_LIST ARGN OR "undefined" IN_LIST ARGN) include(AddCompilerFlags) add_compiler_flags(-fno-omit-frame-pointer -fno-optimize-sibling-calls) endif() add_compile_options(${_fsanitize_option}) add_link_options(${_fsanitize_option}) + + include(TestSuite) + set(SAN_SUPP_DIR "${CMAKE_SOURCE_DIR}/test/sanitizer_suppressions") + if("address" IN_LIST ARGN) + add_test_environment(ASAN_OPTIONS malloc_context_size=0) + add_test_environment(LSAN_OPTIONS "suppressions=${SAN_SUPP_DIR}/lsan") + endif() + if("thread" IN_LIST ARGN) + add_test_environment(TSAN_OPTIONS "suppressions=${SAN_SUPP_DIR}/tsan") + endif() + if("undefined" IN_LIST ARGN) + add_test_environment(UBSAN_OPTIONS "suppressions=${SAN_SUPP_DIR}/ubsan:print_stacktrace=1:halt_on_error=1") + endif() endfunction() diff --git a/cmake/modules/TestSuite.cmake b/cmake/modules/TestSuite.cmake index 64c7b837c6..4f4290fbfd 100644 --- a/cmake/modules/TestSuite.cmake +++ b/cmake/modules/TestSuite.cmake @@ -1,104 +1,120 @@ # Allow to easily build test suites +macro(add_test_environment VARIABLE VALUE) + set_property(GLOBAL APPEND PROPERTY TEST_ENVIRONMENT "${VARIABLE}=${VALUE}") +endmacro() + +function(add_test_custom_target TARGET) + cmake_parse_arguments(ARG "" "" "CUSTOM_TARGET_ARGS;TEST_COMMAND" ${ARGN}) + + get_property(TEST_ENVIRONMENT GLOBAL PROPERTY TEST_ENVIRONMENT) + + add_custom_target(${TARGET} + ${ARG_CUSTOM_TARGET_ARGS} + COMMAND ${CMAKE_COMMAND} -E env ${TEST_ENVIRONMENT} ${ARG_TEST_COMMAND} + ) +endfunction() + # Define a new target property to hold the list of tests associated with a test # suite. This property is named UNIT_TESTS to avoid confusion with the directory # level property TESTS. define_property(TARGET PROPERTY UNIT_TESTS BRIEF_DOCS "List of tests" FULL_DOCS "A list of the tests associated with a test suite" ) macro(get_target_from_suite SUITE TARGET) set(${TARGET} "check-${SUITE}") endmacro() function(create_test_suite_with_parent_targets NAME) get_target_from_suite(${NAME} TARGET) add_custom_target(${TARGET} COMMENT "Running ${NAME} test suite" COMMAND cmake -E echo "PASSED: ${NAME} test suite" ) foreach(PARENT_TARGET ${ARGN}) if(TARGET ${PARENT_TARGET}) add_dependencies(${PARENT_TARGET} ${TARGET}) endif() endforeach() endfunction() macro(create_test_suite NAME) create_test_suite_with_parent_targets(${NAME} check-all check-extended) endmacro() set(TEST_RUNNER_TEMPLATE "${CMAKE_CURRENT_LIST_DIR}/../templates/TestRunner.cmake.in") function(add_test_runner SUITE NAME EXECUTABLE) get_target_from_suite(${SUITE} SUITE_TARGET) set(TARGET "${SUITE_TARGET}-${NAME}") - add_custom_target(${TARGET} - COMMAND + add_test_custom_target(${TARGET} + TEST_COMMAND "${CMAKE_SOURCE_DIR}/cmake/utils/test_wrapper.sh" "$" "${NAME}.log" ${ARGN} - COMMENT "${SUITE}: testing ${NAME}" - DEPENDS ${EXECUTABLE} - VERBATIM + CUSTOM_TARGET_ARGS + COMMENT "${SUITE}: testing ${NAME}" + DEPENDS ${EXECUTABLE} + VERBATIM ) add_dependencies(${SUITE_TARGET} ${TARGET}) endfunction() function(add_test_to_suite SUITE NAME) add_executable(${NAME} EXCLUDE_FROM_ALL ${ARGN}) add_test_runner(${SUITE} ${NAME} ${NAME}) get_target_from_suite(${SUITE} TARGET) set_property( TARGET ${TARGET} APPEND PROPERTY UNIT_TESTS ${NAME} ) endfunction(add_test_to_suite) function(add_boost_unit_tests_to_suite SUITE NAME) cmake_parse_arguments(ARG "" "" "TESTS" ${ARGN} ) get_target_from_suite(${SUITE} SUITE_TARGET) add_executable(${NAME} EXCLUDE_FROM_ALL ${ARG_UNPARSED_ARGUMENTS}) add_dependencies("${SUITE_TARGET}" ${NAME}) foreach(_test_source ${ARG_TESTS}) target_sources(${NAME} PRIVATE "${_test_source}") get_filename_component(_test_name "${_test_source}" NAME_WE) add_test_runner( ${SUITE} ${_test_name} ${NAME} -t "${_test_name}" ) set_property( TARGET ${SUITE_TARGET} APPEND PROPERTY UNIT_TESTS ${_test_name} ) endforeach() find_package(Boost 1.58 REQUIRED unit_test_framework) target_link_libraries(${NAME} Boost::unit_test_framework) # We need to detect if the BOOST_TEST_DYN_LINK flag is required include(CheckCXXSourceCompiles) set(CMAKE_REQUIRED_LIBRARIES Boost::unit_test_framework) check_cxx_source_compiles(" #define BOOST_TEST_DYN_LINK #define BOOST_TEST_MAIN #include " BOOST_TEST_DYN_LINK) if(BOOST_TEST_DYN_LINK) target_compile_definitions(${NAME} PRIVATE BOOST_TEST_DYN_LINK) endif(BOOST_TEST_DYN_LINK) endfunction(add_boost_unit_tests_to_suite) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 26065a9ca0..948ebc1e1d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,133 +1,136 @@ ### # Create config.ini file for tests ### set(abs_top_srcdir ${CMAKE_SOURCE_DIR}) set(abs_top_builddir ${CMAKE_BINARY_DIR}) if(CMAKE_SYSTEM_NAME MATCHES "Windows") set(EXEEXT ".exe") endif() if(NOT BUILD_BITCOIN_WALLET) set(ENABLE_WALLET_TRUE "#") endif() if(NOT BUILD_BITCOIN_TX OR NOT BUILD_BITCOIN_TX) set(BUILD_BITCOIN_UTILS_TRUE "#") endif() if(NOT BUILD_BITCOIN_ZMQ) set(ENABLE_ZMQ_TRUE "#") endif() if(NOT "fuzzer" IN_LIST ENABLE_SANITIZERS) set(ENABLE_FUZZ_TRUE "#") endif() # Create build ini file configure_file(config.ini.in config.ini @ONLY) ### # Setup symlinks for testing ### include(SanitizeHelper) function(make_link file) set(src "${CMAKE_CURRENT_SOURCE_DIR}/${file}") set(dest "${CMAKE_CURRENT_BINARY_DIR}/${file}") # Create the target directory and parents if needed. get_filename_component(dest_dir "${dest}" DIRECTORY) file(MAKE_DIRECTORY "${dest_dir}") add_custom_command( OUTPUT "${dest}" COMMAND ${CMAKE_COMMAND} -E create_symlink "${src}" "${dest}" COMMENT "link ${file}" MAIN_DEPENDENCY "${src}" ) # Add a phony target to make sure the files are linked by default. sanitize_target_name("link-" "${file}" NAME) add_custom_target(${NAME} ALL DEPENDS "${dest}") endfunction() make_link(functional/test_runner.py) make_link(util/bitcoin-util-test.py) make_link(util/rpcauth-test.py) make_link(fuzz/test_runner.py) +include(TestSuite) macro(add_functional_test_check TARGET COMMENT) - add_custom_target(${TARGET} - COMMENT "${COMMENT}" - COMMAND + add_test_custom_target(${TARGET} + TEST_COMMAND "${Python_EXECUTABLE}" ./functional/test_runner.py ${ARGN} - DEPENDS - bitcoind - bitcoin-cli - ${CMAKE_CURRENT_BINARY_DIR}/functional/test_runner.py - USES_TERMINAL - VERBATIM + CUSTOM_TARGET_ARGS + COMMENT "${COMMENT}" + DEPENDS + bitcoind + bitcoin-cli + ${CMAKE_CURRENT_BINARY_DIR}/functional/test_runner.py + USES_TERMINAL + VERBATIM ) endmacro() add_functional_test_check(check-functional "Run the functional tests" ) add_dependencies(check-all check-functional) add_functional_test_check(check-functional-extended "Run the extended functional tests" --extended ) add_dependencies(check-extended check-functional-extended) set(TEST_SUITE_NAME_UPGRADE_ACTIVATED "Bitcoin ABC functional tests with the next upgrade activated") add_functional_test_check(check-functional-upgrade-activated "Run the functional tests with the upgrade activated" --with-phononactivation -n "${TEST_SUITE_NAME_UPGRADE_ACTIVATED}" ) add_dependencies(check-upgrade-activated check-functional-upgrade-activated) add_functional_test_check(check-functional-upgrade-activated-extended "Run the extended functional tests with the upgrade activated" --extended --with-phononactivation -n "${TEST_SUITE_NAME_UPGRADE_ACTIVATED}" ) add_dependencies(check-upgrade-activated-extended check-functional-upgrade-activated-extended) if(BUILD_BITCOIN_TX) - add_custom_target(check-bitcoin-util - COMMENT "Test Bitcoin utilities..." - COMMAND + add_test_custom_target(check-bitcoin-util + TEST_COMMAND "${Python_EXECUTABLE}" ./util/bitcoin-util-test.py - DEPENDS - bitcoin-tx - ${CMAKE_CURRENT_BINARY_DIR}/util/bitcoin-util-test.py + CUSTOM_TARGET_ARGS + COMMENT "Test Bitcoin utilities..." + DEPENDS + bitcoin-tx + ${CMAKE_CURRENT_BINARY_DIR}/util/bitcoin-util-test.py ) add_dependencies(check check-bitcoin-util) endif() add_custom_target(check-rpcauth COMMENT "Test Bitcoin RPC authentication..." COMMAND "${Python_EXECUTABLE}" ./util/rpcauth-test.py DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/util/rpcauth-test.py ) add_dependencies(check check-rpcauth) include(PackageHelper) exclude_from_source_package( # Subdirectories "cache/" "lint/" "sanitizer_suppressions/" ) set_property(DIRECTORY "${CMAKE_SOURCE_DIR}" APPEND PROPERTY ADDITIONAL_CLEAN_FILES "${CMAKE_CURRENT_BINARY_DIR}/tmp" "${CMAKE_CURRENT_BINARY_DIR}/cache")