From 2bedad455934c9b407329d0bf58521cf24510465 Mon Sep 17 00:00:00 2001 From: Daniel Pfeifer Date: Fri, 26 Sep 2025 19:13:10 +0200 Subject: [PATCH] CMake: Add dynamic test discovery --- cmake/module/DiscoverTests.cmake | 74 ++++++++++++++++++++++++++++++++ src/bench/CMakeLists.txt | 10 ++++- src/test/CMakeLists.txt | 50 +++++---------------- 3 files changed, 93 insertions(+), 41 deletions(-) create mode 100644 cmake/module/DiscoverTests.cmake diff --git a/cmake/module/DiscoverTests.cmake b/cmake/module/DiscoverTests.cmake new file mode 100644 index 00000000000..ed795d785d4 --- /dev/null +++ b/cmake/module/DiscoverTests.cmake @@ -0,0 +1,74 @@ +# Copyright (c) 2025-present The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or https://opensource.org/license/mit/. + +# TODO: rework/remove once test discovery is implemented upstream: +# https://gitlab.kitware.com/cmake/cmake/-/issues/26920 +function(discover_tests target) + set(oneValueArgs DISCOVERY_MATCH TEST_NAME_REPLACEMENT TEST_ARGS_REPLACEMENT) + set(multiValueArgs DISCOVERY_ARGS PROPERTIES) + cmake_parse_arguments(PARSE_ARGV 1 arg "" "${oneValueArgs}" "${multiValueArgs}") + + set(file_base "${CMAKE_CURRENT_BINARY_DIR}/${target}") + set(include_file "${file_base}_include.cmake") + + set(properies_content) + list(LENGTH arg_PROPERTIES properties_len) + if(properties_len GREATER "0") + set(properies_content " set_tests_properties(\"\${test_name}\" PROPERTIES\n") + math(EXPR num_properties "${properties_len} / 2") + foreach(i RANGE 0 ${num_properties} 2) + math(EXPR value_index "${i} + 1") + list(GET arg_PROPERTIES ${i} name) + list(GET arg_PROPERTIES ${value_index} value) + string(APPEND properies_content " \"${name}\" \"${value}\"\n") + endforeach() + string(APPEND properies_content " )\n") + endif() + + string(CONCAT include_content + "set(runner [[$]])\n" + "set(launcher [[$]])\n" + "set(emulator [[$<$:$>]])\n" + "\n" + "execute_process(\n" + " COMMAND \${launcher} \${emulator} \${runner} ${arg_DISCOVERY_ARGS}\n" + " OUTPUT_VARIABLE output OUTPUT_STRIP_TRAILING_WHITESPACE\n" + " ERROR_VARIABLE output ERROR_STRIP_TRAILING_WHITESPACE\n" + " RESULT_VARIABLE result\n" + ")\n" + "\n" + "if(NOT result EQUAL 0)\n" + " add_test([[${target}_DISCOVERY_FAILURE]] \${launcher} \${emulator} \${runner} ${arg_DISCOVERY_ARGS})\n" + "else()\n" + " string(REPLACE \"\\n\" \";\" lines \"\${output}\")\n" + " foreach(line IN LISTS lines)\n" + " if(line MATCHES [[${arg_DISCOVERY_MATCH}]])\n" + " string(REGEX REPLACE [[${arg_DISCOVERY_MATCH}]] [[${arg_TEST_NAME_REPLACEMENT}]] test_name \"\${line}\")\n" + " string(REGEX REPLACE [[${arg_DISCOVERY_MATCH}]] [[${arg_TEST_ARGS_REPLACEMENT}]] test_args \"\${line}\")\n" + " separate_arguments(test_args)\n" + " add_test(\"\${test_name}\" \${launcher} \${emulator} \${runner} \${test_args})\n" + ${properies_content} + " endif()\n" + " endforeach()\n" + "endif()\n" + ) + + get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) + if(is_multi_config) + file(GENERATE + OUTPUT "${file_base}_include-$.cmake" + CONTENT "${include_content}" + ) + file(WRITE "${include_file}" + "include(\"${file_base}_include-\${CTEST_CONFIGURATION_TYPE}.cmake\")" + ) + else() + file(GENERATE + OUTPUT "${include_file}" + CONTENT "${include_content}" + ) + endif() + + set_property(DIRECTORY APPEND PROPERTY TEST_INCLUDE_FILES "${include_file}") +endfunction() diff --git a/src/bench/CMakeLists.txt b/src/bench/CMakeLists.txt index 0bf469c79cd..eed209127ce 100644 --- a/src/bench/CMakeLists.txt +++ b/src/bench/CMakeLists.txt @@ -2,6 +2,8 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or https://opensource.org/license/mit/. +include(DiscoverTests) + add_executable(bench_bitcoin bench_bitcoin.cpp bench.cpp @@ -82,8 +84,12 @@ if(ENABLE_WALLET) target_link_libraries(bench_bitcoin bitcoin_wallet) endif() -add_test(NAME bench_sanity_check - COMMAND bench_bitcoin -sanity-check +discover_tests(bench_bitcoin + DISCOVERY_ARGS [[-list]] + DISCOVERY_MATCH [[.+]] + TEST_NAME_REPLACEMENT [[bitcoin.bench.\0]] + TEST_ARGS_REPLACEMENT [[-filter='^\0$']] + PROPERTIES LABELS bench ) install_binary_component(bench_bitcoin INTERNAL) diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index aacefb3f85c..0e369615529 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -2,6 +2,8 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or https://opensource.org/license/mit/. +include(DiscoverTests) + # Do not use generator expressions in test sources because the # SOURCES property is processed to gather test suite macros. add_executable(test_bitcoin @@ -180,44 +182,14 @@ if(ENABLE_IPC) target_link_libraries(test_bitcoin bitcoin_ipc_test bitcoin_ipc) endif() -function(add_boost_test source_file) - if(NOT EXISTS ${source_file}) - return() - endif() - - file(READ "${source_file}" source_file_content) - string(REGEX - MATCHALL "(BOOST_FIXTURE_TEST_SUITE|BOOST_AUTO_TEST_SUITE)\\(([A-Za-z0-9_]+)" - test_suite_macro "${source_file_content}" - ) - list(TRANSFORM test_suite_macro - REPLACE "(BOOST_FIXTURE_TEST_SUITE|BOOST_AUTO_TEST_SUITE)\\(" "" - ) - foreach(test_suite_name IN LISTS test_suite_macro) - add_test(NAME ${test_suite_name} - COMMAND test_bitcoin --run_test=${test_suite_name} --catch_system_error=no --log_level=test_suite -- DEBUG_LOG_OUT - ) - set_property(TEST ${test_suite_name} PROPERTY - SKIP_REGULAR_EXPRESSION - "no test cases matching filter" - "skipping script_assets_test" - "skipping total_ram" - ) - endforeach() -endfunction() - -function(add_all_test_targets) - get_target_property(test_source_dir test_bitcoin SOURCE_DIR) - get_target_property(test_sources test_bitcoin SOURCES) - foreach(test_source ${test_sources}) - cmake_path(IS_RELATIVE test_source result) - if(result) - cmake_path(APPEND test_source_dir ${test_source} OUTPUT_VARIABLE test_source) - endif() - add_boost_test(${test_source}) - endforeach() -endfunction() - -add_all_test_targets() +discover_tests(test_bitcoin + DISCOVERY_ARGS [[--list_content]] + DISCOVERY_MATCH [[^([^ ].*)\*$]] + TEST_NAME_REPLACEMENT [[bitcoin.test.\1]] + TEST_ARGS_REPLACEMENT [[--run_test=\1 --catch_system_error=no --log_level=test_suite -- DEBUG_LOG_OUT]] + PROPERTIES + LABELS "test" + SKIP_REGULAR_EXPRESSION "no test cases matching filter;skipping script_assets_test;skipping total_ram" +) install_binary_component(test_bitcoin INTERNAL)