CMake: Use test wrapper to run android tests

Refactor test adding procedure. It's expected that for each new
platform we prepare test executable and arguments first and then
pass them to common test adding section.

Rename '_qt_internal_wrap_test' to 'qt_internal_create_command_script'
Rework paramerters handling of 'qt_internal_create_command_script',
make them named.

Add 'qt_internal_create_test_script' to add tests and wrap them using
'qt_internal_create_command_script'.

Amends f19266bd02

Fixes: QTBUG-88053
Pick-to: 6.0
Change-Id: I812f4a295005bf3a85cdcb5b8c41180f8249dc96
Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
This commit is contained in:
Alexey Edelev 2020-12-08 15:40:43 +01:00
parent 6198ba217f
commit d5bafc80cd
2 changed files with 168 additions and 78 deletions

View File

@ -125,10 +125,10 @@ define_property(TARGET
"This variable points to the path of the deployment settings JSON file, which holds properties required by androiddeployqt to package the Android app."
)
# Add a test for Android which will be run by the android test runner tool
function(qt_internal_android_add_test target)
# Returns test execution arguments for Android targets
function(qt_internal_android_test_arguments target out_test_runner out_test_arguments)
set(${out_test_runner} "${QT_HOST_PATH}/bin/androidtestrunner" PARENT_SCOPE)
set(deployment_tool "${QT_HOST_PATH}/bin/androiddeployqt")
set(test_runner "${QT_HOST_PATH}/bin/androidtestrunner")
get_target_property(deployment_file ${target} QT_ANDROID_DEPLOYMENT_SETTINGS_FILE)
if (NOT deployment_file)
@ -138,13 +138,13 @@ function(qt_internal_android_add_test target)
set(target_binary_dir "$<TARGET_PROPERTY:${target},BINARY_DIR>")
set(apk_dir "${target_binary_dir}/android-build")
add_test(NAME "${target}"
COMMAND "${test_runner}"
--path "${apk_dir}"
--adb "${ANDROID_SDK_ROOT}/platform-tools/adb"
--skip-install-root
--make "${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target ${target}_make_apk"
--apk "${apk_dir}/${target}.apk"
--verbose
set(${out_test_arguments}
"--path" "${apk_dir}"
"--adb" "${ANDROID_SDK_ROOT}/platform-tools/adb"
"--skip-install-root"
"--make" "${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target ${target}_make_apk"
"--apk" "${apk_dir}/${target}.apk"
"--verbose"
PARENT_SCOPE
)
endfunction()

View File

@ -296,7 +296,8 @@ function(qt_internal_add_test name)
string(REPLACE ";" "\;" plugin_paths_joined "${plugin_paths_joined}")
if (ANDROID)
qt_internal_android_add_test("${name}")
qt_internal_android_test_arguments("${name}" test_executable extra_test_args)
set(test_working_dir "${CMAKE_CURRENT_BINARY_DIR}")
else()
if(arg_QMLTEST AND NOT arg_SOURCES)
set(test_working_dir "${CMAKE_CURRENT_SOURCE_DIR}")
@ -316,20 +317,31 @@ function(qt_internal_add_test name)
#TODO: Should we replace this by TESTARGS environment variable?
list(APPEND extra_test_args "-o" "${name}.xml,xml" "-o" "-,txt")
endif()
endif()
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("${name}" "${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_NO_WRAPPER OR QT_NO_TEST_WRAPPERS)
add_test(NAME "${name}" COMMAND ${test_executable} ${extra_test_args}
WORKING_DIRECTORY "${test_working_dir}")
set_property(TEST "${name}" APPEND PROPERTY
ENVIRONMENT "PATH=${test_env_path}"
"QT_TEST_RUNNING_IN_CTEST=1"
"QT_PLUGIN_PATH=${plugin_paths_joined}"
)
else()
set(test_wrapper_file "${CMAKE_CURRENT_BINARY_DIR}/${name}Wrapper$<CONFIG>.cmake")
qt_internal_create_test_script(NAME "${name}"
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" "${plugin_paths_joined}"
)
endif()
if (arg_QT_TEST_SERVER_LIST)
qt_internal_setup_docker_test_fixture(${name} ${arg_QT_TEST_SERVER_LIST})
endif()
if(arg_QT_TEST_SERVER_LIST AND NOT ANDROID)
qt_internal_setup_docker_test_fixture(${name} ${arg_QT_TEST_SERVER_LIST})
endif()
set_tests_properties("${name}" PROPERTIES RUN_SERIAL "${arg_RUN_SERIAL}" LABELS "${label}")
@ -338,23 +350,13 @@ function(qt_internal_add_test name)
endif()
# Add a ${target}/check makefile target, to more easily test one test.
if(TEST "${name}")
add_custom_target("${name}_check"
VERBATIM
COMMENT "Running ${CMAKE_CTEST_COMMAND} -V -R \"^${name}$\""
COMMAND "${CMAKE_CTEST_COMMAND}" -V -R "^${name}$"
)
if(TARGET "${name}")
add_dependencies("${name}_check" "${name}")
endif()
endif()
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}"
)
add_custom_target("${name}_check"
VERBATIM
COMMENT "Running ${CMAKE_CTEST_COMMAND} -V -R \"^${name}$\""
COMMAND "${CMAKE_CTEST_COMMAND}" -V -R "^${name}$"
)
if(TARGET "${name}")
add_dependencies("${name}_check" "${name}")
endif()
if(ANDROID OR IOS OR WINRT)
@ -414,10 +416,115 @@ function(qt_internal_add_test name)
endfunction()
# This function wraps test with cmake script, that makes possible standalone run with external
# 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 "$<TARGET_FILE:${arg_COMMAND}>")
else()
set(executable_file "${arg_COMMAND}")
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.
if(CMAKE_CROSSCOMPILING)
get_test_property(${arg_NAME} CROSSCOMPILING_EMULATOR 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 wraps COMMAND with cmake script, that makes possible standalone run with external
# arguments.
function(_qt_internal_wrap_test name test_executable extra_test_args test_working_dir)
cmake_parse_arguments(PARSE_ARGV 4 arg "" "" "ENVIRONMENT")
#
# Generated wrapper will be written to OUTPUT_FILE.
# If WORKING_DIRECTORY is not set COMMAND will be executed in CMAKE_CURRENT_BINARY_DIR.
# Variables from ENVIRONMENT will be set before COMMAND execution.
# PRE_RUN and POST_RUN arguments may contain extra cmake code that supposed to be executed before
# and after COMMAND, respectively. Both arguments accept a list of cmake script language
# constructions. Each item of the list will be concantinated into single string with '\n' sepatator.
function(qt_internal_create_command_script)
#This style of parsing keeps ';' in ENVIRONMENT variables
cmake_parse_arguments(PARSE_ARGV 0 arg
""
"OUTPUT_FILE;WORKING_DIRECTORY"
"COMMAND;ENVIRONMENT;PRE_RUN;POST_RUN"
)
if(NOT arg_COMMAND)
message(FATAL_ERROR "qt_internal_create_command_script: COMMAND is not specified")
endif()
if(NOT arg_OUTPUT_FILE)
message(FATAL_ERROR "qt_internal_create_command_script: Wrapper OUTPUT_FILE\
is not specified")
endif()
if(NOT arg_WORKING_DIRECTORY)
set(arg_WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
endif()
set(environment_extras)
set(skipNext false)
@ -450,53 +557,36 @@ function(_qt_internal_wrap_test name test_executable extra_test_args test_workin
#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)
if(NOT crosscompiling_emulator)
set(crosscompiling_emulator "")
else()
qt_internal_wrap_command_arguments(crosscompiling_emulator)
endif()
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")
set(extra_runner "cmd /c")
endif()
qt_internal_wrap_command_arguments(extra_test_args)
if(arg_PRE_RUN)
string(JOIN "\n" pre_run ${arg_PRE_RUN})
endif()
file(GENERATE OUTPUT "${test_wrapper_name}" CONTENT
if(arg_POST_RUN)
string(JOIN "\n" post_run ${arg_POST_RUN})
endif()
file(GENERATE OUTPUT "${arg_OUTPUT_FILE}" CONTENT
"#!${CMAKE_COMMAND} -P
# Qt generated test wrapper for ${name}
# Qt generated command wrapper
${environment_extras}
separate_arguments(test_args NATIVE_COMMAND \"\$ENV{TESTARGS}\")
separate_arguments(test_runner NATIVE_COMMAND \"\$ENV{TESTRUNNER}\")
execute_process(COMMAND ${crosscompiling_emulator} ${extra_test_runner} \${test_runner} \
\"${test_executable_file}\" \${test_args} ${extra_test_args} \
WORKING_DIRECTORY \"${test_working_dir}\" \
RESULT_VARIABLE result)
${pre_run}
execute_process(COMMAND ${extra_runner} ${arg_COMMAND}
WORKING_DIRECTORY \"${arg_WORKING_DIRECTORY}\"
RESULT_VARIABLE result
)
${post_run}
if(NOT result EQUAL 0)
message(FATAL_ERROR)
endif()"
)
)
endfunction()
# This function creates an executable for use as a helper program with tests. Some