# Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause # Simple wrapper around qt_internal_add_executable for benchmarks which insure that # the binary is built under ${CMAKE_CURRENT_BINARY_DIR} and never installed. # See qt_internal_add_executable() for more details. function(qt_internal_add_benchmark target) if(QT_BUILD_TESTS_BATCHED) message(WARNING "Benchmarks won't be batched - unsupported (yet)") endif() cmake_parse_arguments(PARSE_ARGV 1 arg "${__qt_internal_add_executable_optional_args}" "${__qt_internal_add_executable_single_args}" "${__qt_internal_add_executable_multi_args}" ) _qt_internal_validate_all_args_are_parsed(arg) _qt_internal_validate_no_unity_build(arg) qt_remove_args(exec_args ARGS_TO_REMOVE ${target} OUTPUT_DIRECTORY INSTALL_DIRECTORY ALL_ARGS "${__qt_internal_add_executable_optional_args}" "${__qt_internal_add_executable_single_args}" "${__qt_internal_add_executable_multi_args}" ARGS ${ARGV} ) if(NOT arg_OUTPUT_DIRECTORY) set(arg_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") endif() qt_internal_library_deprecation_level(deprecation_define) qt_internal_add_executable(${target} NO_INSTALL # we don't install benchmarks NO_UNITY_BUILD # excluded by default OUTPUT_DIRECTORY "${arg_OUTPUT_DIRECTORY}" # avoid polluting bin directory ${exec_args} ) qt_internal_extend_target(${target} DEFINES ${deprecation_define} ) # Benchmarks on iOS must be app bundles. if(IOS) set_target_properties(${target} PROPERTIES MACOSX_BUNDLE TRUE) endif() qt_internal_add_repo_local_defines(${target}) # Disable the QT_NO_NARROWING_CONVERSIONS_IN_CONNECT define for benchmarks qt_internal_undefine_global_definition(${target} QT_NO_NARROWING_CONVERSIONS_IN_CONNECT) qt_internal_collect_command_environment(benchmark_env_path benchmark_env_plugin_path) # Add a ${target}_benchmark generator target, to run single benchmark more easily. set(benchmark_wrapper_file "${arg_OUTPUT_DIRECTORY}/${target}Wrapper$.cmake") _qt_internal_create_command_script(COMMAND "$" OUTPUT_FILE "${benchmark_wrapper_file}" ENVIRONMENT "PATH" "${benchmark_env_path}" "QT_PLUGIN_PATH" "${benchmark_env_plugin_path}" ) add_custom_target("${target}_benchmark" VERBATIM COMMENT "Running benchmark ${target}" COMMAND "${CMAKE_COMMAND}" "-P" "${benchmark_wrapper_file}" ) add_dependencies("${target}_benchmark" "${target}") # Add benchmark to meta target if it exists. if (TARGET benchmark) add_dependencies("benchmark" "${target}_benchmark") endif() qt_internal_add_test_finalizers("${target}") endfunction() function(qt_internal_add_test_dependencies target) if(QT_BUILD_TESTS_BATCHED) _qt_internal_test_batch_target_name(target) endif() add_dependencies(${target} ${ARGN}) endfunction() # Simple wrapper around qt_internal_add_executable for manual tests which insure that # the binary is built under ${CMAKE_CURRENT_BINARY_DIR} and never installed. # See qt_internal_add_executable() for more details. function(qt_internal_add_manual_test target) qt_internal_add_test(${ARGV} MANUAL) endfunction() # This function will configure the fixture for the network tests that require docker network services # qmake counterpart: qtbase/mkspecs/features/unsupported/testserver.prf function(qt_internal_setup_docker_test_fixture name) # Only Linux is provisioned with docker at this time if (NOT CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") return() endif() option(QT_SKIP_DOCKER_COMPOSE "Skip setting up docker on Linux." OFF) if(QT_SKIP_DOCKER_COMPOSE) return() endif() set(QT_TEST_SERVER_LIST ${ARGN}) set(DNSDOMAIN test-net.qt.local) find_program(QT_DOCKER_COMPOSE docker-compose) if (NOT QT_DOCKER_COMPOSE) message(WARNING "docker-compose was not found. Docker network tests will not be run.") return() endif() if (NOT DEFINED QT_DOCKER_COMPOSE_VERSION) execute_process(COMMAND "${QT_DOCKER_COMPOSE}" --version OUTPUT_VARIABLE QT_DOCKER_COMPOSE_VERSION) string(REPLACE "\n" "" QT_DOCKER_COMPOSE_VERSION "${QT_DOCKER_COMPOSE_VERSION}") set(QT_DOCKER_COMPOSE_VERSION "${QT_DOCKER_COMPOSE_VERSION}" CACHE STRING "docker compose version") endif() find_program(QT_DOCKER docker) if (NOT QT_DOCKER) message(WARNING "docker was not found. Docker network tests will not be run.") return() endif() if (NOT DEFINED QT_DOCKER_TEST_SERVER) execute_process(COMMAND "${QT_DOCKER}" images -aq "qt-test-server-*" OUTPUT_VARIABLE QT_DOCKER_TEST_SERVER) if (NOT QT_DOCKER_TEST_SERVER) message(WARNING "Docker image qt-test-server-* not found.\n" "Run the provisioning script (coin/provisioning/.../testserver/docker_testserver.sh) in advance\n" "Docker network tests will not be run.") return() endif() set(QT_DOCKER_TEST_SERVER "ON" CACHE BOOL "docker qt-test-server-* present") endif() target_compile_definitions("${name}" PRIVATE QT_TEST_SERVER QT_TEST_SERVER_NAME QT_TEST_SERVER_DOMAIN=\"${DNSDOMAIN}\" ) if(DEFINED QT_TESTSERVER_COMPOSE_FILE) set(TESTSERVER_COMPOSE_FILE ${QT_TESTSERVER_COMPOSE_FILE}) elseif(QNX) set(TESTSERVER_COMPOSE_FILE "${QT_SOURCE_TREE}/tests/testserver/docker-compose-qemu-bridge-network.yml") else() set(TESTSERVER_COMPOSE_FILE "${QT_SOURCE_TREE}/tests/testserver/docker-compose-bridge-network.yml") endif() # Bring up test servers and make sure the services are ready. add_test(NAME ${name}-setup COMMAND "${QT_DOCKER_COMPOSE}" -f ${TESTSERVER_COMPOSE_FILE} up --build -d --force-recreate --timeout 1 ${QT_TEST_SERVER_LIST} ) # Stop and remove test servers after testing. add_test(NAME ${name}-cleanup COMMAND "${QT_DOCKER_COMPOSE}" -f ${TESTSERVER_COMPOSE_FILE} down --timeout 1 ) set_tests_properties(${name}-setup PROPERTIES FIXTURES_SETUP ${name}-docker) set_tests_properties(${name}-cleanup PROPERTIES FIXTURES_CLEANUP ${name}-docker) set_tests_properties(${name} PROPERTIES FIXTURES_REQUIRED ${name}-docker) foreach(test_name ${name} ${name}-setup ${name}-cleanup) set_property(TEST "${test_name}" APPEND PROPERTY ENVIRONMENT "testserver=${QT_DOCKER_COMPOSE_VERSION}") set_property(TEST "${test_name}" APPEND PROPERTY ENVIRONMENT TEST_DOMAIN=${DNSDOMAIN}) set_property(TEST "${test_name}" APPEND PROPERTY ENVIRONMENT "SHARED_DATA=${QT_MKSPECS_DIR}/features/data/testserver") set_property(TEST "${test_name}" APPEND PROPERTY ENVIRONMENT SHARED_SERVICE=bridge-network) endforeach() endfunction() function(qt_internal_get_test_batch out) get_property(batched_list GLOBAL PROPERTY _qt_batched_test_list_property) set(${out} ${batched_list} PARENT_SCOPE) endfunction() function(qt_internal_prepare_test_target_flags version_arg exceptions_text gui_text) cmake_parse_arguments(arg "EXCEPTIONS;NO_EXCEPTIONS;GUI" "VERSION" "" ${ARGN}) if (arg_VERSION) set(${version_arg} VERSION "${arg_VERSION}" PARENT_SCOPE) endif() # Qt modules get compiled without exceptions enabled by default. # However, testcases should be still built with exceptions. set(${exceptions_text} "EXCEPTIONS" PARENT_SCOPE) if (${arg_NO_EXCEPTIONS} OR WASM) set(${exceptions_text} "" PARENT_SCOPE) endif() if (${arg_GUI}) set(${gui_text} "GUI" PARENT_SCOPE) endif() endfunction() function(qt_internal_get_test_arg_definitions optional_args single_value_args multi_value_args) set(${optional_args} RUN_SERIAL EXCEPTIONS NO_EXCEPTIONS GUI QMLTEST CATCH LOWDPI NO_WRAPPER BUILTIN_TESTDATA MANUAL NO_BATCH NO_INSTALL BUNDLE_ANDROID_OPENSSL_LIBS PARENT_SCOPE ) set(${single_value_args} OUTPUT_DIRECTORY WORKING_DIRECTORY TIMEOUT VERSION PARENT_SCOPE ) set(${multi_value_args} QML_IMPORTPATH TESTDATA QT_TEST_SERVER_LIST ${__default_private_args} ${__default_public_args} PARENT_SCOPE ) endfunction() function(qt_internal_add_test_to_batch batch_name name) qt_internal_get_test_arg_definitions(optional_args single_value_args multi_value_args) cmake_parse_arguments( arg "${optional_args}" "${single_value_args}" "${multi_value_args}" ${ARGN}) qt_internal_prepare_test_target_flags(version_arg exceptions_text gui_text ${ARGN}) _qt_internal_validate_no_unity_build(arg) _qt_internal_test_batch_target_name(target) # Lazy-init the test batch if(NOT TARGET ${target}) qt_internal_library_deprecation_level(deprecation_define) qt_internal_add_executable(${target} ${exceptions_text} ${gui_text} ${version_arg} NO_INSTALL OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/build_dir" SOURCES "${QT_CMAKE_DIR}/qbatchedtestrunner.in.cpp" DEFINES QTEST_BATCH_TESTS ${deprecation_define} INCLUDE_DIRECTORIES ${private_includes} LIBRARIES ${QT_CMAKE_EXPORT_NAMESPACE}::Core ${QT_CMAKE_EXPORT_NAMESPACE}::Test ${QT_CMAKE_EXPORT_NAMESPACE}::TestPrivate # Add GUI by default so that the plugins link properly with non-standalone # build of tests. Plugin handling is currently only done in # qt_internal_add_executable if Gui is present. This should be reevaluated with # multiple batches. ${QT_CMAKE_EXPORT_NAMESPACE}::Gui ) set_property(TARGET ${target} PROPERTY _qt_has_exceptions ${arg_EXCEPTIONS}) set_property(TARGET ${target} PROPERTY _qt_has_gui ${arg_GUI}) set_property(TARGET ${target} PROPERTY _qt_has_lowdpi ${arg_LOWDPI}) set_property(TARGET ${target} PROPERTY _qt_version ${version_arg}) set_property(TARGET ${target} PROPERTY _qt_is_test_executable TRUE) set_property(TARGET ${target} PROPERTY _qt_is_manual_test ${arg_MANUAL}) else() # Check whether the args match with the batch. Some differences between # flags cannot be reconciled - one should not combine these tests into # a single binary. qt_internal_get_target_property( batch_has_exceptions ${target} _qt_has_exceptions) if(NOT ${batch_has_exceptions} STREQUAL ${arg_EXCEPTIONS}) qt_internal_get_test_batch(test_batch_contents) message(FATAL_ERROR "Conflicting exceptions declaration between test \ batch (${test_batch_contents}) and ${name}") endif() qt_internal_get_target_property(batch_has_gui ${target} _qt_has_gui) if(NOT ${batch_has_gui} STREQUAL ${arg_GUI}) qt_internal_get_test_batch(test_batch_contents) message(FATAL_ERROR "Conflicting gui declaration between test batch \ (${test_batch_contents}) and ${name}") endif() qt_internal_get_target_property( batch_has_lowdpi ${target} _qt_has_lowdpi) if(NOT ${batch_has_lowdpi} STREQUAL ${arg_LOWDPI}) qt_internal_get_test_batch(test_batch_contents) message(FATAL_ERROR "Conflicting lowdpi declaration between test batch \ (${test_batch_contents}) and ${name}") endif() qt_internal_get_target_property(batch_version ${target} _qt_version) if(NOT "${batch_version} " STREQUAL " " AND NOT "${version_arg} " STREQUAL " " AND NOT "${batch_version} " STREQUAL "${version_arg} ") qt_internal_get_test_batch(test_batch_contents) message(FATAL_ERROR "Conflicting version declaration between test \ batch ${test_batch_contents} (${batch_version}) and ${name} (${version_arg})") endif() endif() get_property(batched_test_list GLOBAL PROPERTY _qt_batched_test_list_property) if(NOT batched_test_list) set_property(GLOBAL PROPERTY _qt_batched_test_list_property "") set(batched_test_list "") endif() list(PREPEND batched_test_list ${name}) set_property(GLOBAL PROPERTY _qt_batched_test_list_property ${batched_test_list}) # Test batching produces single executable which can result in one source file being added # multiple times (with different definitions) to one translation unit. This is not supported by # CMake so instead we try to detect such situation and rename file every time it's added # to the build more than once. This avoids filenames collisions in one translation unit. get_property(batched_test_sources_list GLOBAL PROPERTY _qt_batched_test_sources_list_property) if(NOT batched_test_sources_list) set_property(GLOBAL PROPERTY _qt_batched_test_sources_list_property "") set(batched_test_sources_list "") endif() foreach(source ${arg_SOURCES}) set(source_path ${source}) if(${source} IN_LIST batched_test_sources_list) set(new_filename ${name}.cpp) configure_file(${source} ${new_filename}) set(source_path ${CMAKE_CURRENT_BINARY_DIR}/${new_filename}) set(skip_automoc ON) list(APPEND arg_SOURCES ${source_path}) else() set(skip_automoc OFF) list(APPEND batched_test_sources_list ${source}) endif() set_source_files_properties(${source_path} TARGET_DIRECTORY ${target} PROPERTIES SKIP_AUTOMOC ${skip_automoc} COMPILE_DEFINITIONS "BATCHED_TEST_NAME=\"${name}\";${arg_DEFINES}") endforeach() set_property(GLOBAL PROPERTY _qt_batched_test_sources_list_property ${batched_test_sources_list}) # Merge the current test with the rest of the batch qt_internal_extend_target(${target} INCLUDE_DIRECTORIES ${arg_INCLUDE_DIRECTORIES} PUBLIC_LIBRARIES ${arg_PUBLIC_LIBRARIES} LIBRARIES ${arg_LIBRARIES} SOURCES ${arg_SOURCES} COMPILE_OPTIONS ${arg_COMPILE_OPTIONS} COMPILE_FLAGS ${arg_COMPILE_FLAGS} LINK_OPTIONS ${arg_LINK_OPTIONS} MOC_OPTIONS ${arg_MOC_OPTIONS} ENABLE_AUTOGEN_TOOLS ${arg_ENABLE_AUTOGEN_TOOLS} DISABLE_AUTOGEN_TOOLS ${arg_DISABLE_AUTOGEN_TOOLS} NO_UNITY_BUILD # Tests should not be built using UNITY_BUILD ) set(${batch_name} ${target} PARENT_SCOPE) # Add a dummy target so that new tests don't have problems with a nonexistent # target when calling cmake functions. # The batch tests that include this target will compile, but may fail to work. # Manual action is required then. add_custom_target(${name}) # Add the dependency to the dummy target so that it is indirectly added to the test batch # dependencies. add_dependencies(${target} ${name}) endfunction() # Checks whether the test 'name' is present in the test batch. See QT_BUILD_TESTS_BATCHED. # The result of the check is placed in the 'out' variable. function(qt_internal_is_in_test_batch out name) set(${out} FALSE PARENT_SCOPE) if(QT_BUILD_TESTS_BATCHED) get_property(batched_test_list GLOBAL PROPERTY _qt_batched_test_list_property) if("${name}" IN_LIST batched_test_list) set(${out} TRUE PARENT_SCOPE) endif() endif() endfunction() function(qt_internal_is_skipped_test out name) get_target_property(is_skipped_test ${name} _qt_is_skipped_test) set(${out} ${is_skipped_test} PARENT_SCOPE) endfunction() function(qt_internal_set_skipped_test name) set_target_properties(${name} PROPERTIES _qt_is_skipped_test TRUE) endfunction() function(qt_internal_is_qtbase_test out) get_filename_component(dir "${CMAKE_CURRENT_BINARY_DIR}" ABSOLUTE) set(${out} FALSE PARENT_SCOPE) while(TRUE) get_filename_component(filename "${dir}" NAME) if("${filename}" STREQUAL "qtbase") set(${out} TRUE PARENT_SCOPE) break() endif() set(prev_dir "${dir}") get_filename_component(dir "${dir}" DIRECTORY) if("${dir}" STREQUAL "${prev_dir}") break() endif() endwhile() endfunction() function(qt_internal_get_batched_test_arguments out testname) if(WASM) # Add a query string to the runner document, so that the script therein # knows which test to run in response to launching the testcase by ctest. list(APPEND args "qbatchedtest") if(CMAKE_BUILD_TYPE STREQUAL "Debug") list(APPEND args "qvisualoutput") endif() else() # Simply add the test name in case of standard executables. list(APPEND args "${testname}") endif() set(${out} ${args} PARENT_SCOPE) endfunction() # This function creates a CMake test target with the specified name for use with CTest. # # All tests are wrapped with cmake script that supports TESTARGS and TESTRUNNER environment # variables handling. Endpoint wrapper may be used standalone as cmake script to run tests e.g.: # TESTARGS="-o result.xml,junitxml" TESTRUNNER="testrunner --arg" ./tst_simpleTestWrapper.cmake # On non-UNIX machine you may need to use 'cmake -P' explicitly to execute wrapper. # You may avoid test wrapping by either passing NO_WRAPPER option or switching QT_NO_TEST_WRAPPERS # to ON. This is helpful if you want to use internal CMake tools within tests, like memory or # sanitizer checks. See https://cmake.org/cmake/help/v3.19/manual/ctest.1.html#ctest-memcheck-step # Arguments: # BUILTIN_TESTDATA # The option forces adding the provided TESTDATA to resources. # MANUAL # The option indicates that the test is a manual test. function(qt_internal_add_test name) qt_internal_get_test_arg_definitions(optional_args single_value_args multi_value_args) cmake_parse_arguments(PARSE_ARGV 1 arg "${optional_args}" "${single_value_args}" "${multi_value_args}" ) _qt_internal_validate_all_args_are_parsed(arg) _qt_internal_validate_no_unity_build(arg) set(batch_current_test FALSE) if(QT_BUILD_TESTS_BATCHED AND NOT arg_NO_BATCH AND NOT arg_QMLTEST AND NOT arg_MANUAL AND ("${QT_STANDALONE_TEST_PATH}" STREQUAL "" OR DEFINED ENV{QT_BATCH_STANDALONE_TESTS})) set(batch_current_test TRUE) endif() if(batch_current_test OR (QT_BUILD_TESTS_BATCHED AND arg_QMLTEST)) if (QT_SUPERBUILD OR DEFINED ENV{TESTED_MODULE_COIN}) set(is_qtbase_test FALSE) if(QT_SUPERBUILD) qt_internal_is_qtbase_test(is_qtbase_test) elseif($ENV{TESTED_MODULE_COIN} STREQUAL "qtbase") set(is_qtbase_test TRUE) endif() if(NOT is_qtbase_test) file(GENERATE OUTPUT "dummy${name}.cpp" CONTENT "int main() { return 0; }") # Add a dummy target to tackle some potential problems qt_internal_add_executable(${name} SOURCES "dummy${name}.cpp") # Batched tests outside of qtbase are unsupported and skipped qt_internal_set_skipped_test(${name}) return() endif() endif() endif() if (NOT arg_OUTPUT_DIRECTORY) set(arg_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") endif() set(private_includes "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}" "$" ) set(testname "${name}") if(arg_PUBLIC_LIBRARIES) message(WARNING "qt_internal_add_test's PUBLIC_LIBRARIES option is deprecated, and will be " "removed in a future Qt version. Use the LIBRARIES option instead.") endif() if(batch_current_test) qt_internal_add_test_to_batch(name ${name} ${ARGN}) elseif(arg_SOURCES) if(QT_BUILD_TESTS_BATCHED AND arg_QMLTEST) message(WARNING "QML tests won't be batched - unsupported (yet)") endif() # Handle cases where we have a qml test without source files list(APPEND private_includes ${arg_INCLUDE_DIRECTORIES}) qt_internal_prepare_test_target_flags(version_arg exceptions_text gui_text ${ARGN}) qt_internal_library_deprecation_level(deprecation_define) qt_internal_add_executable("${name}" ${exceptions_text} ${gui_text} ${version_arg} NO_INSTALL OUTPUT_DIRECTORY "${arg_OUTPUT_DIRECTORY}" SOURCES "${arg_SOURCES}" INCLUDE_DIRECTORIES ${private_includes} DEFINES ${arg_DEFINES} ${deprecation_define} LIBRARIES ${arg_LIBRARIES} ${arg_PUBLIC_LIBRARIES} ${QT_CMAKE_EXPORT_NAMESPACE}::Core ${QT_CMAKE_EXPORT_NAMESPACE}::Test COMPILE_OPTIONS ${arg_COMPILE_OPTIONS} LINK_OPTIONS ${arg_LINK_OPTIONS} MOC_OPTIONS ${arg_MOC_OPTIONS} ENABLE_AUTOGEN_TOOLS ${arg_ENABLE_AUTOGEN_TOOLS} DISABLE_AUTOGEN_TOOLS ${arg_DISABLE_AUTOGEN_TOOLS} NO_UNITY_BUILD # Tests should not be built using UNITY_BUILD ) qt_internal_add_repo_local_defines(${name}) # Disable the QT_NO_NARROWING_CONVERSIONS_IN_CONNECT define for tests qt_internal_undefine_global_definition(${name} QT_NO_NARROWING_CONVERSIONS_IN_CONNECT) # Manual tests can be bundle apps if(NOT arg_MANUAL) # Tests should not be bundles on macOS even if arg_GUI is true, because some tests make # assumptions about the location of helper processes, and those paths would be different # if a test is built as a bundle. set_property(TARGET "${name}" PROPERTY MACOSX_BUNDLE FALSE) # The same goes for WIN32_EXECUTABLE, but because it will detach from the console window # and not print anything. set_property(TARGET "${name}" PROPERTY WIN32_EXECUTABLE FALSE) endif() # Tests on iOS must be app bundles. if(IOS) set_target_properties(${name} PROPERTIES MACOSX_BUNDLE TRUE) endif() # QMLTest specifics qt_internal_extend_target("${name}" CONDITION arg_QMLTEST LIBRARIES ${QT_CMAKE_EXPORT_NAMESPACE}::QuickTest ) qt_internal_extend_target("${name}" CONDITION arg_QMLTEST AND NOT ANDROID DEFINES QUICK_TEST_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}" ) qt_internal_extend_target("${name}" CONDITION arg_QMLTEST AND ANDROID DEFINES QUICK_TEST_SOURCE_DIR=":/" ) # Android requires Qt::Gui so add it by default for tests qt_internal_extend_target("${name}" CONDITION ANDROID LIBRARIES ${QT_CMAKE_EXPORT_NAMESPACE}::Gui ) set_target_properties(${name} PROPERTIES _qt_is_test_executable TRUE) set_target_properties(${name} PROPERTIES _qt_is_manual_test ${arg_MANUAL}) endif() foreach(path IN LISTS arg_QML_IMPORTPATH) list(APPEND extra_test_args "-import" "${path}") endforeach() # Generate a label in the form tests/auto/foo/bar/tst_baz # and use it also for XML output set(label_base_directory "${PROJECT_SOURCE_DIR}") if (QT_SUPERBUILD) # Prepend repository name for qt5 builds, so that tests can be run for # individual repositories. set(label_base_directory "${label_base_directory}/..") endif() file(RELATIVE_PATH label "${label_base_directory}" "${CMAKE_CURRENT_SOURCE_DIR}/${name}") if (arg_LOWDPI) target_compile_definitions("${name}" PUBLIC TESTCASE_LOWDPI) if (MACOS) set_property(TARGET "${name}" PROPERTY MACOSX_BUNDLE_INFO_PLIST "${QT_MKSPECS_DIR}/macx-clang/Info.plist.disable_highdpi") set_property(TARGET "${name}" PROPERTY PROPERTY MACOSX_BUNDLE TRUE) endif() endif() if (ANDROID) if(arg_BUNDLE_ANDROID_OPENSSL_LIBS) if(NOT OPENSSL_ROOT_DIR) message(WARNING "The argument BUNDLE_ANDROID_OPENSSL_LIBS is set " "but OPENSSL_ROOT_DIR parameter is not set.") else() if(EXISTS "${OPENSSL_ROOT_DIR}/${CMAKE_ANDROID_ARCH_ABI}/libcrypto_3.so") set_property(TARGET ${name} APPEND PROPERTY QT_ANDROID_EXTRA_LIBS "${OPENSSL_ROOT_DIR}/${CMAKE_ANDROID_ARCH_ABI}/libcrypto_3.so" "${OPENSSL_ROOT_DIR}/${CMAKE_ANDROID_ARCH_ABI}/libssl_3.so") else() message(STATUS "Test should bundle OpenSSL libraries but they are not found." " This is fine if OpenSSL was built statically.") endif() endif() endif() qt_internal_android_test_arguments("${name}" test_executable extra_test_args) set(test_working_dir "${CMAKE_CURRENT_BINARY_DIR}") elseif(QNX) set(test_working_dir "") set(test_executable "${name}") elseif(WASM) # The test script expects an html file. In case of batched tests, the # version specialized for running batches has to be supplied. if(batch_current_test) get_target_property(batch_output_dir ${name} RUNTIME_OUTPUT_DIRECTORY) set(test_executable "${batch_output_dir}/${name}.html") else() set(test_executable "${name}.html") endif() list(APPEND extra_test_args "quseemrun") list(APPEND extra_test_args "qtestname=${testname}") list(APPEND extra_test_args "--silence_timeout=60") # TODO: Add functionality to specify browser list(APPEND extra_test_args "--browser=chrome") list(APPEND extra_test_args "--browser_args=\"--password-store=basic\"") list(APPEND extra_test_args "--kill_exit") # Tests may require asyncify if they use exec(). Enable asyncify for # batched tests since this is the configuration used on the CI system. # Optimize for size (-Os), since asyncify tends to make the resulting # binary very large if(batch_current_test) target_link_options("${name}" PRIVATE "SHELL:-s ASYNCIFY" "-Os") endif() # This tells cmake to run the tests with this script, since wasm files can't be # executed directly set_property(TARGET "${name}" PROPERTY CROSSCOMPILING_EMULATOR "emrun") else() if(arg_QMLTEST AND NOT arg_SOURCES) set(test_working_dir "${CMAKE_CURRENT_SOURCE_DIR}") set(test_executable ${QT_CMAKE_EXPORT_NAMESPACE}::qmltestrunner) else() if (arg_WORKING_DIRECTORY) set(test_working_dir "${arg_WORKING_DIRECTORY}") elseif(arg_OUTPUT_DIRECTORY) set(test_working_dir "${arg_OUTPUT_DIRECTORY}") else() set(test_working_dir "${CMAKE_CURRENT_BINARY_DIR}") endif() set(test_executable "${name}") endif() endif() if(NOT arg_MANUAL) if(batch_current_test) qt_internal_get_batched_test_arguments(batched_test_args ${testname}) list(PREPEND extra_test_args ${batched_test_args}) elseif(WASM AND CMAKE_BUILD_TYPE STREQUAL "Debug") list(PREPEND extra_test_args "qvisualoutput") endif() qt_internal_collect_command_environment(test_env_path test_env_plugin_path) if(arg_NO_WRAPPER OR QT_NO_TEST_WRAPPERS) if(QT_BUILD_TESTS_BATCHED) message(FATAL_ERROR "Wrapperless tests are unspupported with test batching") endif() add_test(NAME "${testname}" COMMAND ${test_executable} ${extra_test_args} WORKING_DIRECTORY "${test_working_dir}") set_property(TEST "${testname}" APPEND PROPERTY ENVIRONMENT "PATH=${test_env_path}" "QT_TEST_RUNNING_IN_CTEST=1" "QT_PLUGIN_PATH=${test_env_plugin_path}" ) else() set(test_wrapper_file "${CMAKE_CURRENT_BINARY_DIR}/${testname}Wrapper$.cmake") qt_internal_create_test_script(NAME "${testname}" COMMAND "${test_executable}" ARGS "${extra_test_args}" WORKING_DIRECTORY "${test_working_dir}" OUTPUT_FILE "${test_wrapper_file}" ENVIRONMENT "QT_TEST_RUNNING_IN_CTEST" 1 "PATH" "${test_env_path}" "QT_PLUGIN_PATH" "${test_env_plugin_path}" ) endif() if(arg_QT_TEST_SERVER_LIST AND NOT ANDROID) qt_internal_setup_docker_test_fixture(${testname} ${arg_QT_TEST_SERVER_LIST}) endif() set_tests_properties("${testname}" PROPERTIES RUN_SERIAL "${arg_RUN_SERIAL}" LABELS "${label}") if(arg_TIMEOUT) set_tests_properties(${testname} PROPERTIES TIMEOUT ${arg_TIMEOUT}) endif() # Add a ${target}/check makefile target, to more easily test one test. set(test_config_options "") get_cmake_property(is_multi_config GENERATOR_IS_MULTI_CONFIG) if(is_multi_config) set(test_config_options -C $) endif() add_custom_target("${testname}_check" VERBATIM COMMENT "Running ${CMAKE_CTEST_COMMAND} -V -R \"^${name}$\" ${test_config_options}" COMMAND "${CMAKE_CTEST_COMMAND}" -V -R "^${name}$" ${test_config_options} ) if(TARGET "${name}") add_dependencies("${testname}_check" "${name}") if(ANDROID) add_dependencies("${testname}_check" "${name}_make_apk") endif() endif() endif() if(ANDROID OR IOS OR WASM OR INTEGRITY OR arg_BUILTIN_TESTDATA) set(builtin_testdata TRUE) endif() if(builtin_testdata) # Safe guard against qml only tests, no source files == no target if (TARGET "${name}") target_compile_definitions("${name}" PRIVATE BUILTIN_TESTDATA) foreach(testdata IN LISTS arg_TESTDATA) list(APPEND builtin_files ${testdata}) endforeach() foreach(file IN LISTS builtin_files) set_source_files_properties(${file} PROPERTIES QT_SKIP_QUICKCOMPILER TRUE ) endforeach() if(batch_current_test) set(blacklist_path "BLACKLIST") if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${blacklist_path}") get_target_property(blacklist_files ${name} _qt_blacklist_files) if(NOT blacklist_files) set_target_properties(${name} PROPERTIES _qt_blacklist_files "") set(blacklist_files "") cmake_language(EVAL CODE "cmake_language(DEFER DIRECTORY \"${CMAKE_SOURCE_DIR}\" CALL \"_qt_internal_finalize_batch\" \"${name}\") ") endif() list(PREPEND blacklist_files "${CMAKE_CURRENT_SOURCE_DIR}/${blacklist_path}") set_target_properties(${name} PROPERTIES _qt_blacklist_files "${blacklist_files}") endif() else() set(blacklist_path "BLACKLIST") if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${blacklist_path}") list(APPEND builtin_files ${blacklist_path}) endif() endif() list(REMOVE_DUPLICATES builtin_files) # Skip Qt quick compiler when embedding test resources foreach(file IN LISTS builtin_files) set_source_files_properties(${file} PROPERTIES QT_SKIP_QUICKCOMPILER TRUE ) endforeach() if(builtin_files) qt_internal_add_resource(${name} "${testname}_testdata_builtin" PREFIX "/" FILES ${builtin_files} BASE ${CMAKE_CURRENT_SOURCE_DIR}) endif() endif() else() # Install test data, when tests are built in-tree or as standalone tests, but not as a # single standalone test, which is checked by the existence of the QT_TOP_LEVEL_SOURCE_DIR # variable. # TODO: Shouldn't we also handle the single standalone test case? # TODO: Does installing even makes sense, given where QFINDTESTDATA looks for installed # test data, and where we end up installing it? See QTBUG-117098. if(QT_TOP_LEVEL_SOURCE_DIR) foreach(testdata IN LISTS arg_TESTDATA) set(testdata "${CMAKE_CURRENT_SOURCE_DIR}/${testdata}") # Get the relative source dir for each test data entry, because it might contain a # subdirectory. file(RELATIVE_PATH relative_path_to_test_project "${QT_TOP_LEVEL_SOURCE_DIR}" "${testdata}") get_filename_component(relative_path_to_test_project "${relative_path_to_test_project}" DIRECTORY) qt_path_join(testdata_install_dir ${QT_INSTALL_DIR} "${relative_path_to_test_project}") if (IS_DIRECTORY "${testdata}") qt_install( DIRECTORY "${testdata}" DESTINATION "${testdata_install_dir}") else() qt_install( FILES "${testdata}" DESTINATION "${testdata_install_dir}") endif() endforeach() endif() endif() qt_internal_add_test_finalizers("${name}") endfunction() # This function adds test with specified NAME and wraps given test COMMAND with standalone cmake # script. # # NAME must be compatible with add_test function, since it's propagated as is. # COMMAND might be either target or path to executable. When test is called either by ctest or # directly by 'cmake -P path/to/scriptWrapper.cmake', COMMAND will be executed in specified # WORKING_DIRECTORY with arguments specified in ARGS. # # See also _qt_internal_create_command_script for details. function(qt_internal_create_test_script) #This style of parsing keeps ';' in ENVIRONMENT variables cmake_parse_arguments(PARSE_ARGV 0 arg "" "NAME;COMMAND;OUTPUT_FILE;WORKING_DIRECTORY" "ARGS;ENVIRONMENT;PRE_RUN;POST_RUN" ) if(NOT arg_COMMAND) message(FATAL_ERROR "qt_internal_create_test_script: Test COMMAND is not specified") endif() if(NOT arg_NAME) message(FATAL_ERROR "qt_internal_create_test_script: Test NAME is not specified") endif() if(NOT arg_OUTPUT_FILE) message(FATAL_ERROR "qt_internal_create_test_script: Test Wrapper OUTPUT_FILE\ is not specified") endif() if(arg_PRE_RUN) message(WARNING "qt_internal_create_test_script: PRE_RUN is not acceptable argument\ for this function. Will be ignored") endif() if(arg_POST_RUN) message(WARNING "qt_internal_create_test_script: POST_RUN is not acceptable argument\ for this function. Will be ignored") endif() if(arg_ARGS) set(command_args ${arg_ARGS})# Avoid "${arg_ARGS}" usage and let cmake expand string to # semicolon-separated list qt_internal_wrap_command_arguments(command_args) endif() if(TARGET ${arg_COMMAND}) set(executable_file "$") else() set(executable_file "${arg_COMMAND}") endif() set(executable_name ${arg_NAME}) qt_internal_is_in_test_batch(is_in_batch ${executable_name}) if(is_in_batch) _qt_internal_test_batch_target_name(executable_name) endif() add_test(NAME "${arg_NAME}" COMMAND "${CMAKE_COMMAND}" "-P" "${arg_OUTPUT_FILE}" WORKING_DIRECTORY "${arg_WORKING_DIRECTORY}") # If crosscompiling is enabled, we should avoid run cmake in emulator environment. # Prepend emulator to test command in generated cmake script instead. Keep in mind that # CROSSCOMPILING_EMULATOR don't check if actual cross compilation is configured, # emulator is prepended independently. set(crosscompiling_emulator "") if(CMAKE_CROSSCOMPILING AND TARGET ${executable_name}) get_target_property(crosscompiling_emulator ${executable_name} CROSSCOMPILING_EMULATOR) if(NOT crosscompiling_emulator) set(crosscompiling_emulator "") else() qt_internal_wrap_command_arguments(crosscompiling_emulator) endif() endif() _qt_internal_create_command_script(COMMAND "${crosscompiling_emulator} \${env_test_runner} \ \"${executable_file}\" \${env_test_args} ${command_args}" OUTPUT_FILE "${arg_OUTPUT_FILE}" WORKING_DIRECTORY "${arg_WORKING_DIRECTORY}" ENVIRONMENT ${arg_ENVIRONMENT} PRE_RUN "separate_arguments(env_test_args NATIVE_COMMAND \ \"\$ENV{TESTARGS}\")" "separate_arguments(env_test_runner NATIVE_COMMAND \ \"\$ENV{TESTRUNNER}\")" ) endfunction() # This function creates an executable for use as a helper program with tests. Some # tests launch separate programs to test certain input/output behavior. # Specify OVERRIDE_OUTPUT_DIRECTORY if you don't want to place the helper in the parent directory, # in which case you should specify OUTPUT_DIRECTORY "/foo/bar" manually. function(qt_internal_add_test_helper name) set(qt_add_test_helper_optional_args "OVERRIDE_OUTPUT_DIRECTORY" ) cmake_parse_arguments(PARSE_ARGV 1 arg "${qt_add_test_helper_optional_args};${__qt_internal_add_executable_optional_args}" "${__qt_internal_add_executable_single_args}" "${__qt_internal_add_executable_multi_args}" ) _qt_internal_validate_all_args_are_parsed(arg) qt_remove_args(forward_args ARGS_TO_REMOVE "${name}" ${qt_add_test_helper_optional_args} ALL_ARGS ${qt_add_test_helper_optional_args} ${__qt_internal_add_executable_optional_args} ${__qt_internal_add_executable_single_args} ${__qt_internal_add_executable_multi_args} ARGS ${ARGV} ) set(extra_args_to_pass) if(NOT arg_OVERRIDE_OUTPUT_DIRECTORY) _qt_internal_test_batch_target_name(test_batch_target_name) if(QT_BUILD_TESTS_BATCHED AND TARGET ${test_batch_target_name}) get_target_property( test_batch_output_dir ${test_batch_target_name} RUNTIME_OUTPUT_DIRECTORY) set(extra_args_to_pass OUTPUT_DIRECTORY "${test_batch_output_dir}") else() set(extra_args_to_pass OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/..") endif() endif() qt_internal_add_executable("${name}" NO_INSTALL NO_UNITY_BUILD # excluded by default ${extra_args_to_pass} ${forward_args}) # Disable the QT_NO_NARROWING_CONVERSIONS_IN_CONNECT define for test helpers qt_internal_undefine_global_definition(${name} QT_NO_NARROWING_CONVERSIONS_IN_CONNECT) endfunction() function(qt_internal_wrap_command_arguments argument_list) list(TRANSFORM ${argument_list} REPLACE "^(.+)$" "[=[\\1]=]") list(JOIN ${argument_list} " " ${argument_list}) set(${argument_list} "${${argument_list}}" PARENT_SCOPE) endfunction() function(qt_internal_collect_command_environment out_path out_plugin_path) # Get path to /bin, as well as CMAKE_INSTALL_PREFIX/bin, and # combine them with the PATH environment variable. # It's needed on Windows to find the shared libraries and plugins. # qt_relocatable_install_prefix is dynamically computed from the location of where the Qt CMake # package is found. # The regular CMAKE_INSTALL_PREFIX can be different for example when building standalone tests. # Any given CMAKE_INSTALL_PREFIX takes priority over qt_relocatable_install_prefix for the # PATH environment variable. set(install_prefixes "${CMAKE_INSTALL_PREFIX}") if(QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX) list(APPEND install_prefixes "${QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX}") endif() file(TO_NATIVE_PATH "${CMAKE_CURRENT_BINARY_DIR}" test_env_path) foreach(install_prefix ${install_prefixes}) file(TO_NATIVE_PATH "${install_prefix}/${INSTALL_BINDIR}" install_prefix) set(test_env_path "${test_env_path}${QT_PATH_SEPARATOR}${install_prefix}") endforeach() set(test_env_path "${test_env_path}${QT_PATH_SEPARATOR}$ENV{PATH}") string(REPLACE ";" "\;" test_env_path "${test_env_path}") set(${out_path} "${test_env_path}" PARENT_SCOPE) # Add the install prefix to list of plugin paths when doing a prefix build if(NOT QT_INSTALL_DIR) foreach(install_prefix ${install_prefixes}) file(TO_NATIVE_PATH "${install_prefix}/${INSTALL_BINDIR}" install_prefix) list(APPEND plugin_paths "${install_prefix}") endforeach() endif() #TODO: Collect all paths from known repositories when performing a super # build. file(TO_NATIVE_PATH "${PROJECT_BINARY_DIR}/${INSTALL_PLUGINSDIR}" install_pluginsdir) list(APPEND plugin_paths "${install_pluginsdir}") list(JOIN plugin_paths "${QT_PATH_SEPARATOR}" plugin_paths_joined) string(REPLACE ";" "\;" plugin_paths_joined "${plugin_paths_joined}") set(${out_plugin_path} "${plugin_paths_joined}" PARENT_SCOPE) endfunction() function(qt_internal_add_test_finalizers target) # It might not be safe to run all the finalizers of _qt_internal_finalize_executable # within the context of a Qt build (not a user project) when targeting a host build. # At least one issue is missing qmlimportscanner at configure time. # For now, we limit it to iOS, where it was tested to work, an we know that host tools # should already be built and available. if(IOS) qt_add_list_file_finalizer(_qt_internal_finalize_executable "${target}") endif() endfunction()