# Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file LICENSE.rst or https://cmake.org/licensing for details. cmake_minimum_required(VERSION 4.2) include("${CMAKE_CURRENT_LIST_DIR}/GoogleTest/ParseTestList.cmake") macro(write_test_to_file) # Store the gtest test name before messing with these strings set(gtest_name ${current_test_suite}.${current_test_name}) set(pretty_test_suite ${current_test_suite}) set(pretty_test_name ${current_test_name}) # Handle disabled tests set(maybe_DISABLED "") if(pretty_test_suite MATCHES "^DISABLED_" OR pretty_test_name MATCHES "^DISABLED_") set(maybe_DISABLED "DISABLED YES") string(REGEX REPLACE "^DISABLED_" "" pretty_test_suite "${pretty_test_suite}") string(REGEX REPLACE "^DISABLED_" "" pretty_test_name "${pretty_test_name}") endif() if (NOT current_test_value_param STREQUAL "" AND NOT arg_NO_PRETTY_VALUES) # If the test name contains a value parameter name (part of the string after last slash), and this # name is an integer which indicates that it's the default name generated by googletest, replace it by # the value parameter name provided separately if("${pretty_test_name}" MATCHES "^(.*)/[0-9]+$") set(pretty_test_name "${CMAKE_MATCH_1}/${current_test_value_param}") endif() endif() if(NOT current_test_type_param STREQUAL "") # Parse type param name from suite name if(pretty_test_suite MATCHES "^(.+)/(.+)$") set(pretty_test_suite "${CMAKE_MATCH_1}") set(current_type_param_name "${CMAKE_MATCH_2}") else() set(current_type_param_name "") endif() if (NOT arg_NO_PRETTY_TYPES) string(APPEND pretty_test_name "<${current_test_type_param}>") elseif(NOT current_type_param_name STREQUAL "") string(APPEND pretty_test_name "<${current_type_param_name}>") endif() endif() set(test_name_template "@prefix@@pretty_test_suite@.@pretty_test_name@@suffix@") string(CONFIGURE "${test_name_template}" testname) if(NOT "${arg_TEST_XML_OUTPUT_DIR}" STREQUAL "") set(TEST_XML_OUTPUT_PARAM "--gtest_output=xml:${arg_TEST_XML_OUTPUT_DIR}/${prefix}${gtest_name}${suffix}.xml") else() set(TEST_XML_OUTPUT_PARAM "") endif() # unescape [] if(open_sb) string(REPLACE "${open_sb}" "[" testname "${testname}") endif() if(close_sb) string(REPLACE "${close_sb}" "]" testname "${testname}") endif() set(guarded_testname "${open_guard}${testname}${close_guard}") string(APPEND script "add_test(${guarded_testname} ${launcherArgs}") foreach(arg IN ITEMS "${arg_TEST_EXECUTABLE}" "--gtest_filter=${gtest_name}" "--gtest_also_run_disabled_tests" ${TEST_XML_OUTPUT_PARAM} ) if(arg MATCHES "[^-./:a-zA-Z0-9_]") string(APPEND script " [==[${arg}]==]") else() string(APPEND script " ${arg}") endif() endforeach() if(arg_TEST_EXTRA_ARGS) list(JOIN arg_TEST_EXTRA_ARGS "]==] [==[" extra_args) string(APPEND script " [==[${extra_args}]==]") endif() string(APPEND script ")\n") set(maybe_LOCATION "") if(NOT current_test_file STREQUAL "" AND NOT current_test_line STREQUAL "") set(maybe_LOCATION "DEF_SOURCE_LINE [==[${current_test_file}:${current_test_line}]==]") endif() set(maybe_properties) if(arg_TEST_PROPERTIES) list(JOIN arg_TEST_PROPERTIES "]] [[" maybe_properties) set(maybe_properties "[[${maybe_properties}]]") endif() string(APPEND script "set_tests_properties(${guarded_testname}\n" " PROPERTIES\n" " ${maybe_DISABLED}\n" " ${maybe_LOCATION}\n" " WORKING_DIRECTORY [==[${arg_TEST_WORKING_DIR}]==]\n" " SKIP_REGULAR_EXPRESSION [==[\\[ SKIPPED \\]]==]\n" " ${maybe_properties}\n" ")\n" ) # possibly unbalanced square brackets render lists invalid so skip such # tests in ${arg_TEST_LIST} if(NOT "${testname}" MATCHES [=[(\[|\])]=]) # escape ; string(REPLACE [[;]] [[\\;]] testname "${testname}") list(APPEND tests_buffer "${testname}") list(LENGTH tests_buffer tests_buffer_length) if(tests_buffer_length GREATER "250") # Chunk updates to the final "tests" variable, keeping the # "tests_buffer" variable that we append each test to relatively # small. This mitigates worsening performance impacts for the # corner case of having many thousands of tests. list(APPEND tests "${tests_buffer}") set(tests_buffer "") endif() endif() # If we've built up a sizable script so far, write it out as a chunk now # so we don't accumulate a massive string to write at the end string(LENGTH "${script}" script_len) if(${script_len} GREATER "50000") file(APPEND "${arg_CTEST_FILE}" "${script}") set(script "") endif() endmacro() function(gtest_discover_tests_impl) set(options "") set(oneValueArgs NO_PRETTY_TYPES # These two take a value, unlike gtest_discover_tests() NO_PRETTY_VALUES # TEST_TARGET TEST_EXECUTABLE TEST_WORKING_DIR TEST_PREFIX TEST_SUFFIX TEST_LIST CTEST_FILE TEST_DISCOVERY_TIMEOUT TEST_XML_OUTPUT_DIR TEST_JSON_OUTPUT_DIR TEST_FILTER ) set(multiValueArgs TEST_EXTRA_ARGS TEST_DISCOVERY_EXTRA_ARGS TEST_PROPERTIES TEST_EXECUTOR ) cmake_parse_arguments(PARSE_ARGV 0 arg "${options}" "${oneValueArgs}" "${multiValueArgs}" ) set(prefix "${arg_TEST_PREFIX}") set(suffix "${arg_TEST_SUFFIX}") set(script) set(tests) set(tests_buffer "") # If a file at ${arg_CTEST_FILE} already exists, we overwrite it. file(REMOVE "${arg_CTEST_FILE}") set(filter) if(arg_TEST_FILTER) set(filter "--gtest_filter=${arg_TEST_FILTER}") endif() # CMP0178 has already been handled in gtest_discover_tests(), so we only need # to implement NEW behavior here. This means preserving empty arguments for # TEST_EXECUTOR. For OLD or WARN, gtest_discover_tests() already removed any # empty arguments. set(launcherArgs "") if(NOT "${arg_TEST_EXECUTOR}" STREQUAL "") list(JOIN arg_TEST_EXECUTOR "]==] [==[" launcherArgs) set(launcherArgs "[==[${launcherArgs}]==]") endif() # Run test executable to get list of available tests if(NOT EXISTS "${arg_TEST_EXECUTABLE}") message(FATAL_ERROR "Specified test executable does not exist.\n" " Path: '${arg_TEST_EXECUTABLE}'" ) endif() set(discovery_extra_args "") if(NOT "${arg_TEST_DISCOVERY_EXTRA_ARGS}" STREQUAL "") list(JOIN arg_TEST_DISCOVERY_EXTRA_ARGS "]==] [==[" discovery_extra_args) set(discovery_extra_args "[==[${discovery_extra_args}]==]") endif() # Avoid a potential race condition for the POST_BUILD case when multiple # calls are made to gtest_discover_tests() for different targets but the same # working directory. For PRE_TEST, we're always executing serially during the # ctest setup phase, so there is no race condition there, but POST_BUILD can # lead to this code path being run in parallel. Use a hash to avoid potential # problems with very long target names. string(SHA256 target_hash "${arg_TEST_TARGET}") string(SUBSTRING "${target_hash}" 0 10 target_hash) set(json_file "${arg_TEST_JSON_OUTPUT_DIR}/cmake_test_discovery_${target_hash}.json" ) # Remove json file to make sure we don't pick up an outdated one file(REMOVE "${json_file}") cmake_language(EVAL CODE "execute_process( COMMAND ${launcherArgs} [==[${arg_TEST_EXECUTABLE}]==] --gtest_list_tests [==[--gtest_output=json:${json_file}]==] ${filter} ${discovery_extra_args} WORKING_DIRECTORY [==[${arg_TEST_WORKING_DIR}]==] TIMEOUT ${arg_TEST_DISCOVERY_TIMEOUT} OUTPUT_VARIABLE output RESULT_VARIABLE result )" ) if(NOT ${result} EQUAL 0) string(REPLACE "\n" "\n " output "${output}") if(arg_TEST_EXECUTOR) set(path "${arg_TEST_EXECUTOR} ${arg_TEST_EXECUTABLE}") else() set(path "${arg_TEST_EXECUTABLE}") endif() message(FATAL_ERROR "Error running test executable.\n" " Path: '${path}'\n" " Working directory: '${arg_TEST_WORKING_DIR}'\n" " Timeout: '${arg_TEST_DISCOVERY_TIMEOUT}'\n" " Result: ${result}\n" " Output:\n" " ${output}\n" ) endif() if(EXISTS "${json_file}") parse_tests_from_json("${json_file}" write_test_to_file) else() # gtest < 1.8.1, and all gtest compiled with GTEST_HAS_FILE_SYSTEM=0, don't # recognize the --gtest_output=json option, and issue a warning or error on # stdout about it being unrecognized, but still return an exit code 0 for # success. All versions report the test list on stdout whether # --gtest_output=json is recognized or not. # NOTE: Because we are calling a macro, we don't want to pass "output" as # an argument because it messes up the contents passed through due to the # different escaping, etc. that gets applied. We rely on it picking up the # "output" variable we have already set here. parse_tests_from_output(write_test_to_file) endif() if(NOT tests_buffer STREQUAL "") list(APPEND tests "${tests_buffer}") endif() # Create a list of all discovered tests, which users may use to e.g. set # properties on the tests list(JOIN tests "]==] [==[" tests) string(APPEND script "set(${arg_TEST_LIST} [==[${tests}]==])\n") # Write remaining content to the CTest script file(APPEND "${arg_CTEST_FILE}" "${script}") endfunction()