diff options
-rw-r--r-- | cmake/QtTestHelpers.cmake | 173 |
1 files changed, 137 insertions, 36 deletions
diff --git a/cmake/QtTestHelpers.cmake b/cmake/QtTestHelpers.cmake index 8a11705d8e..3405eee83a 100644 --- a/cmake/QtTestHelpers.cmake +++ b/cmake/QtTestHelpers.cmake @@ -141,10 +141,18 @@ function(qt_internal_setup_docker_test_fixture name) 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,xunitxml" 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 function(qt_internal_add_test name) # EXCEPTIONS is a noop as they are enabled by default. qt_parse_all_arguments(arg "qt_add_test" - "RUN_SERIAL;EXCEPTIONS;NO_EXCEPTIONS;GUI;QMLTEST;CATCH;LOWDPI" + "RUN_SERIAL;EXCEPTIONS;NO_EXCEPTIONS;GUI;QMLTEST;CATCH;LOWDPI;NO_WRAPPER" "OUTPUT_DIRECTORY;WORKING_DIRECTORY;TIMEOUT;VERSION" "QML_IMPORTPATH;TESTDATA;QT_TEST_SERVER_LIST;${__default_private_args};${__default_public_args}" ${ARGN} ) @@ -240,6 +248,42 @@ function(qt_internal_add_test name) endif() endif() + # Get path to <qt_relocatable_install_prefix>/bin, as well as CMAKE_INSTALL_PREFIX/bin, then + # prepend them to 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}") + + # 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}") + if (ANDROID) qt_internal_android_add_test("${name}") else() @@ -258,15 +302,25 @@ function(qt_internal_add_test name) endif() if (NOT arg_CATCH) - list(APPEND test_outputs "-o" "${name}.xml,xml" "-o" "-,txt") + #TODO: Should we replace this by TESTARGS environment variable? + list(APPEND extra_test_args "-o" "${name}.xml,xml" "-o" "-,txt") endif() - add_test(NAME "${name}" COMMAND ${test_executable} ${extra_test_args} ${test_outputs} WORKING_DIRECTORY "${test_working_dir}") + if(arg_NO_WRAPPER OR QT_NO_TEST_WRAPPERS) + add_test(NAME "${name}" COMMAND ${test_executable} ${extra_test_args} + WORKING_DIRECTORY "${test_working_dir}") + else() + _qt_internal_wrap_test("${test_executable}" "${extra_test_args}" "${test_working_dir}" + ENVIRONMENT "QT_TEST_RUNNING_IN_CTEST" 1 + "PATH" "${test_env_path}" + "QT_PLUGIN_PATH" "${plugin_paths_joined}") + endif() if (arg_QT_TEST_SERVER_LIST) qt_internal_setup_docker_test_fixture(${name} ${arg_QT_TEST_SERVER_LIST}) endif() endif() + set_tests_properties("${name}" PROPERTIES RUN_SERIAL "${arg_RUN_SERIAL}" LABELS "${label}") if (arg_TIMEOUT) set_tests_properties(${name} PROPERTIES TIMEOUT ${arg_TIMEOUT}) @@ -284,41 +338,14 @@ function(qt_internal_add_test name) endif() endif() - # Get path to <qt_relocatable_install_prefix>/bin, as well as CMAKE_INSTALL_PREFIX/bin, then - # prepend them to 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() - - set(test_env_path "PATH=${CMAKE_CURRENT_BINARY_DIR}") - foreach(install_prefix ${install_prefixes}) - set(test_env_path "${test_env_path}${QT_PATH_SEPARATOR}${install_prefix}/${INSTALL_BINDIR}") - endforeach() - set(test_env_path "${test_env_path}${QT_PATH_SEPARATOR}$ENV{PATH}") - string(REPLACE ";" "\;" test_env_path "${test_env_path}") - set_property(TEST "${name}" APPEND PROPERTY ENVIRONMENT "${test_env_path}") - set_property(TEST "${name}" APPEND PROPERTY ENVIRONMENT "QT_TEST_RUNNING_IN_CTEST=1") - - # Add the install prefix to list of plugin paths when doing a prefix build - if(NOT QT_INSTALL_DIR) - foreach(install_prefix ${install_prefixes}) - list(APPEND plugin_paths "${install_prefix}/${INSTALL_PLUGINSDIR}") - endforeach() + if(ANDROID OR arg_NO_WRAPPER OR QT_NO_TEST_WRAPPERS) + set_property(TEST "${name}" APPEND PROPERTY ENVIRONMENT + "PATH=${test_env_path}" + "QT_TEST_RUNNING_IN_CTEST=1" + "QT_PLUGIN_PATH=${plugin_paths_joined}" + ) endif() - #TODO: Collect all paths from known repositories when performing a super - # build. - list(APPEND plugin_paths "${PROJECT_BINARY_DIR}/${INSTALL_PLUGINSDIR}") - list(JOIN plugin_paths "${QT_PATH_SEPARATOR}" plugin_paths_joined) - set_property(TEST "${name}" APPEND PROPERTY ENVIRONMENT "QT_PLUGIN_PATH=${plugin_paths_joined}") - if(ANDROID OR IOS OR WINRT) set(builtin_testdata TRUE) endif() @@ -376,6 +403,80 @@ function(qt_internal_add_test name) endfunction() +# This function wraps test with cmake script, that makes possible standalone run with external +# arguments. +function(_qt_internal_wrap_test test_executable extra_test_args test_working_dir) + cmake_parse_arguments(PARSE_ARGV 3 arg "" "" "ENVIRONMENT") + + set(environment_extras) + set(skipNext false) + if(arg_ENVIRONMENT) + list(LENGTH arg_ENVIRONMENT length) + math(EXPR length "${length} - 1") + foreach(envIdx RANGE ${length}) + if(skipNext) + set(skipNext FALSE) + continue() + endif() + + set(envVariable "") + set(envValue "") + + list(GET arg_ENVIRONMENT ${envIdx} envVariable) + math(EXPR envIdx "${envIdx} + 1") + if (envIdx LESS_EQUAL ${length}) + list(GET arg_ENVIRONMENT ${envIdx} envValue) + endif() + + if(NOT "${envVariable}" STREQUAL "") + set(environment_extras "${environment_extras}\nset(ENV{${envVariable}} \ +\"${envValue}\")") + endif() + set(skipNext TRUE) + endforeach() + endif() + + #Escaping environment variables before expand them by file GENERATE + string(REPLACE "\\" "\\\\" environment_extras "${environment_extras}") + + set(test_wrapper_name "${CMAKE_CURRENT_BINARY_DIR}/${name}Wrapper$<CONFIG>.cmake") + add_test(NAME "${name}" COMMAND "${CMAKE_COMMAND}" "-P" "${test_wrapper_name}" + WORKING_DIRECTORY "${test_working_dir}") + + if(TARGET ${test_executable}) + set(test_executable_file "$<TARGET_FILE:${test_executable}>") + else() + set(test_executable_file "${test_executable}") + endif() + + # 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. + if(CMAKE_CROSSCOMPILING) + get_test_property(${name} CROSSCOMPILING_EMULATOR crosscompiling_emulator) + endif() + + if(WIN32) + # It's necessary to call actual test inside 'cmd.exe', because 'execute_process' uses + # SW_HIDE to avoid showing a console window, it affects other GUI as well. + # See https://gitlab.kitware.com/cmake/cmake/-/issues/17690 for details. + set(extra_test_runner "cmd /c") + endif() + + file(GENERATE OUTPUT "${test_wrapper_name}" CONTENT +"#!${CMAKE_COMMAND} -P +# Qt generated test wrapper for ${name} + +${environment_extras} +execute_process(COMMAND ${crosscompiling_emulator} ${extra_test_runner} \$ENV{TESTRUNNER} \"${test_executable_file}\" \ +\$ENV{TESTARGS} ${extra_test_args} WORKING_DIRECTORY \"${test_working_dir}\" \ +RESULT_VARIABLE result) +if(NOT result EQUAL 0) + message(FATAL_ERROR) +endif()" +) +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. |