Implement the batch_tests feature
An approach of test batching (joining multiple tests into a single binary) has been taken, due to long linking times/binary size on certain platforms, including WASM. This change adds a new feature 'batch_test_support' in Qt testlib. Based on the value of the feature, test batching may become enabled with the -batch-tests switch. Batching works for every target added via qt_internal_add_test. When first such target is being processed, a new combined target for all of the future test sources is created under the name of 'test_batch'. CMake attempts to merge the parameters of each of the tests, and some basic checks are run for parameter differences that are impossible to reconcile. On the C++ level, convenience macros instantiating the tests are redefined when batch_tests is on. The new, changed behavior triggered by the changes in the macros registers the tests in a central test registry, where they are available for execution based solely on their test name. The test name is interoperable with the names CMake is aware of, so CTest is able to run the tests one by one in the combined binary. Task-number: QTBUG-105273 Change-Id: I2b6071d58be16979bd967eab2d405249f5a4e658 Reviewed-by: Topi Reiniö <topi.reinio@qt.io>
This commit is contained in:
parent
8446655f24
commit
8d728a0ed9
@ -286,6 +286,7 @@ qt_copy_or_install(FILES
|
||||
cmake/QtWriteArgsFile.cmake
|
||||
cmake/modulecppexports.h.in
|
||||
cmake/modulecppexports_p.h.in
|
||||
cmake/qbatchedtestrunner.in.cpp
|
||||
DESTINATION "${__GlobalConfig_install_dir}"
|
||||
)
|
||||
|
||||
|
@ -87,6 +87,9 @@ set(QT_BUILD_MINIMAL_STATIC_TESTS @QT_BUILD_MINIMAL_STATIC_TESTS@ CACHE BOOL
|
||||
set(QT_BUILD_MINIMAL_ANDROID_MULTI_ABI_TESTS @QT_BUILD_MINIMAL_ANDROID_MULTI_ABI_TESTS@ CACHE BOOL
|
||||
"Build minimal subset of tests for Android multi-ABI Qt builds")
|
||||
|
||||
set(QT_BUILD_TESTS_BATCHED @QT_BUILD_TESTS_BATCHED@ CACHE BOOL
|
||||
"Should all tests be batched into a single binary.")
|
||||
|
||||
set(QT_BUILD_TESTS_BY_DEFAULT @QT_BUILD_TESTS_BY_DEFAULT@ CACHE BOOL
|
||||
"Should tests be built as part of the default 'all' target.")
|
||||
set(QT_BUILD_EXAMPLES_BY_DEFAULT @QT_BUILD_EXAMPLES_BY_DEFAULT@ CACHE BOOL
|
||||
|
@ -2,6 +2,14 @@
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
function(qt_internal_add_resource target resourceName)
|
||||
if(NOT TARGET "${target}")
|
||||
qt_internal_is_in_test_batch(in_batch ${target})
|
||||
if(NOT in_batch)
|
||||
message(FATAL_ERROR "Trying to add resource to a non-existing target \"${target}\".")
|
||||
endif()
|
||||
qt_internal_test_batch_target_name(target)
|
||||
endif()
|
||||
|
||||
# Don't try to add resources when cross compiling, and the target is actually a host target
|
||||
# (like a tool).
|
||||
qt_is_imported_target("${target}" is_imported)
|
||||
|
@ -225,6 +225,18 @@ if(QT_BUILD_STANDALONE_TESTS)
|
||||
endif()
|
||||
set(BUILD_TESTING ${QT_BUILD_TESTS} CACHE INTERNAL "")
|
||||
|
||||
set(_qt_batch_tests OFF)
|
||||
if(INPUT_batch_tests)
|
||||
set(_qt_batch_tests ON)
|
||||
endif()
|
||||
option(QT_BUILD_TESTS_BATCHED "Link all tests into a single binary." ${_qt_batch_tests})
|
||||
|
||||
if(QT_BUILD_TESTS AND QT_BUILD_TESTS_BATCHED AND CMAKE_VERSION VERSION_LESS "3.18")
|
||||
message(FATAL_ERROR
|
||||
"Test batching requires at least CMake 3.18, due to requiring per-source "
|
||||
"TARGET_DIRECTORY assignments.")
|
||||
endif()
|
||||
|
||||
# QT_BUILD_TOOLS_WHEN_CROSSCOMPILING -> QT_FORCE_BUILD_TOOLS
|
||||
# pre-6.4 compatibility flag (remove sometime in the future)
|
||||
if(CMAKE_CROSSCOMPILING AND QT_BUILD_TOOLS_WHEN_CROSSCOMPILING)
|
||||
|
@ -4,15 +4,20 @@
|
||||
# This function can be used to add sources/libraries/etc. to the specified CMake target
|
||||
# if the provided CONDITION evaluates to true.
|
||||
function(qt_internal_extend_target target)
|
||||
if(NOT TARGET "${target}")
|
||||
qt_internal_is_in_test_batch(in_batch ${target})
|
||||
if(NOT in_batch)
|
||||
message(FATAL_ERROR "Trying to extend a non-existing target \"${target}\".")
|
||||
endif()
|
||||
qt_internal_test_batch_target_name(target)
|
||||
endif()
|
||||
|
||||
# Don't try to extend_target when cross compiling an imported host target (like a tool).
|
||||
qt_is_imported_target("${target}" is_imported)
|
||||
if(is_imported)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if (NOT TARGET "${target}")
|
||||
message(FATAL_ERROR "Trying to extend non-existing target \"${target}\".")
|
||||
endif()
|
||||
qt_parse_all_arguments(arg "qt_extend_target" "" "PRECOMPILED_HEADER"
|
||||
"CONDITION;${__default_public_args};${__default_private_args};${__default_private_module_args};COMPILE_FLAGS;NO_PCH_SOURCES" ${ARGN})
|
||||
if ("x${arg_CONDITION}" STREQUAL x)
|
||||
@ -884,8 +889,12 @@ endfunction()
|
||||
# qt_internal_add_global_definition function for a specific 'target'.
|
||||
function(qt_internal_undefine_global_definition target)
|
||||
if(NOT TARGET ${target})
|
||||
qt_internal_is_in_test_batch(in_batch ${target})
|
||||
if(NOT ${in_batch})
|
||||
message(FATAL_ERROR "${target} is not a target.")
|
||||
endif()
|
||||
qt_internal_test_batch_target_name(target)
|
||||
endif()
|
||||
|
||||
if("${ARGN}" STREQUAL "")
|
||||
message(FATAL_ERROR "The function expects at least one definition as an argument.")
|
||||
|
@ -5,6 +5,9 @@
|
||||
# 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()
|
||||
|
||||
qt_parse_all_arguments(arg "qt_add_benchmark"
|
||||
"${__qt_internal_add_executable_optional_args}"
|
||||
@ -72,6 +75,17 @@ function(qt_internal_add_benchmark target)
|
||||
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()
|
||||
|
||||
function(qt_internal_test_batch_target_name out)
|
||||
set(${out} "test_batch" PARENT_SCOPE)
|
||||
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.
|
||||
@ -192,6 +206,169 @@ function(qt_internal_setup_docker_test_fixture name)
|
||||
|
||||
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})
|
||||
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
|
||||
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_test_batch_target_name(target)
|
||||
|
||||
# Lazy-init the test batch
|
||||
if(NOT TARGET ${target})
|
||||
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
|
||||
INCLUDE_DIRECTORIES ${private_includes}
|
||||
LIBRARIES ${QT_CMAKE_EXPORT_NAMESPACE}::Core
|
||||
${QT_CMAKE_EXPORT_NAMESPACE}::Test
|
||||
${QT_CMAKE_EXPORT_NAMESPACE}::TestPrivate
|
||||
)
|
||||
|
||||
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})
|
||||
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})
|
||||
|
||||
# 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}
|
||||
DEFINES ${arg_DEFINES}
|
||||
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})
|
||||
|
||||
foreach(source ${arg_SOURCES})
|
||||
# We define the test name which is later used to launch this test using
|
||||
# commandline parameters. Target directory is that of the target test_batch,
|
||||
# otherwise the batch won't honor our choices of compile definitions.
|
||||
set_source_files_properties(${source}
|
||||
TARGET_DIRECTORY ${target}
|
||||
PROPERTIES COMPILE_DEFINITIONS
|
||||
"BATCHED_TEST_NAME=\"${name}\"")
|
||||
endforeach()
|
||||
set(${batch_name} ${target} PARENT_SCOPE)
|
||||
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()
|
||||
|
||||
# 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
|
||||
@ -204,31 +381,8 @@ endfunction()
|
||||
# Arguments:
|
||||
# BUILTIN_TESTDATA the option forces adding the provided TESTDATA to resources.
|
||||
function(qt_internal_add_test name)
|
||||
# EXCEPTIONS is a noop as they are enabled by default.
|
||||
set(optional_args
|
||||
RUN_SERIAL
|
||||
EXCEPTIONS
|
||||
NO_EXCEPTIONS
|
||||
GUI
|
||||
QMLTEST
|
||||
CATCH
|
||||
LOWDPI
|
||||
NO_WRAPPER
|
||||
BUILTIN_TESTDATA
|
||||
)
|
||||
set(single_value_args
|
||||
OUTPUT_DIRECTORY
|
||||
WORKING_DIRECTORY
|
||||
TIMEOUT
|
||||
VERSION
|
||||
)
|
||||
set(multi_value_args
|
||||
QML_IMPORTPATH
|
||||
TESTDATA
|
||||
QT_TEST_SERVER_LIST
|
||||
${__default_private_args}
|
||||
${__default_public_args}
|
||||
)
|
||||
qt_internal_get_test_arg_definitions(optional_args single_value_args multi_value_args)
|
||||
|
||||
qt_parse_all_arguments(arg "qt_add_test"
|
||||
"${optional_args}"
|
||||
"${single_value_args}"
|
||||
@ -240,20 +394,13 @@ function(qt_internal_add_test name)
|
||||
set(arg_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
endif()
|
||||
|
||||
# Qt modules get compiled without exceptions enabled by default.
|
||||
# However, testcases should be still built with exceptions.
|
||||
set(exceptions_text "EXCEPTIONS")
|
||||
if (${arg_NO_EXCEPTIONS})
|
||||
set(exceptions_text "")
|
||||
endif()
|
||||
set(private_includes
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}"
|
||||
"$<BUILD_INTERFACE:${QT_BUILD_DIR}/include>"
|
||||
)
|
||||
|
||||
if (${arg_GUI})
|
||||
set(gui_text "GUI")
|
||||
endif()
|
||||
|
||||
if (arg_VERSION)
|
||||
set(version_arg VERSION "${arg_VERSION}")
|
||||
endif()
|
||||
set(testname "${name}")
|
||||
|
||||
if(arg_PUBLIC_LIBRARIES)
|
||||
message(WARNING
|
||||
@ -261,15 +408,16 @@ function(qt_internal_add_test name)
|
||||
"removed in a future Qt version. Use the LIBRARIES option instead.")
|
||||
endif()
|
||||
|
||||
if(QT_BUILD_TESTS_BATCHED AND NOT arg_QMLTEST)
|
||||
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
|
||||
if (arg_SOURCES)
|
||||
set(private_includes
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}"
|
||||
"$<BUILD_INTERFACE:${QT_BUILD_DIR}/include>"
|
||||
${arg_INCLUDE_DIRECTORIES}
|
||||
)
|
||||
list(APPEND private_includes ${arg_INCLUDE_DIRECTORIES})
|
||||
|
||||
qt_internal_prepare_test_target_flags(version_arg exceptions_text gui_text ${ARGN})
|
||||
qt_internal_add_executable("${name}"
|
||||
${exceptions_text}
|
||||
${gui_text}
|
||||
@ -391,16 +539,20 @@ function(qt_internal_add_test name)
|
||||
qt_internal_collect_command_environment(test_env_path test_env_plugin_path)
|
||||
|
||||
if(arg_NO_WRAPPER OR QT_NO_TEST_WRAPPERS)
|
||||
add_test(NAME "${name}" COMMAND ${test_executable} ${extra_test_args}
|
||||
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 "${name}" APPEND PROPERTY
|
||||
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}/${name}Wrapper$<CONFIG>.cmake")
|
||||
qt_internal_create_test_script(NAME "${name}"
|
||||
set(test_wrapper_file "${CMAKE_CURRENT_BINARY_DIR}/${testname}Wrapper$<CONFIG>.cmake")
|
||||
qt_internal_create_test_script(NAME "${testname}"
|
||||
COMMAND "${test_executable}"
|
||||
ARGS "${extra_test_args}"
|
||||
WORKING_DIRECTORY "${test_working_dir}"
|
||||
@ -412,12 +564,12 @@ function(qt_internal_add_test name)
|
||||
endif()
|
||||
|
||||
if(arg_QT_TEST_SERVER_LIST AND NOT ANDROID)
|
||||
qt_internal_setup_docker_test_fixture(${name} ${arg_QT_TEST_SERVER_LIST})
|
||||
qt_internal_setup_docker_test_fixture(${testname} ${arg_QT_TEST_SERVER_LIST})
|
||||
endif()
|
||||
|
||||
set_tests_properties("${name}" PROPERTIES RUN_SERIAL "${arg_RUN_SERIAL}" LABELS "${label}")
|
||||
if (arg_TIMEOUT)
|
||||
set_tests_properties(${name} PROPERTIES TIMEOUT ${arg_TIMEOUT})
|
||||
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.
|
||||
@ -427,15 +579,15 @@ function(qt_internal_add_test name)
|
||||
if(is_multi_config)
|
||||
set(test_config_options -C $<CONFIG>)
|
||||
endif()
|
||||
add_custom_target("${name}_check"
|
||||
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("${name}_check" "${name}")
|
||||
add_dependencies("${testname}_check" "${name}")
|
||||
if(ANDROID)
|
||||
add_dependencies("${name}_check" "${name}_make_apk")
|
||||
add_dependencies("${testname}_check" "${name}_make_apk")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@ -466,8 +618,8 @@ function(qt_internal_add_test name)
|
||||
)
|
||||
endforeach()
|
||||
|
||||
if (builtin_files)
|
||||
qt_internal_add_resource(${name} "${name}_testdata_builtin"
|
||||
if(builtin_files)
|
||||
qt_internal_add_resource(${name} "${testname}_testdata_builtin"
|
||||
PREFIX "/"
|
||||
FILES ${builtin_files}
|
||||
BASE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
@ -551,6 +703,10 @@ for this function. Will be ignored")
|
||||
set(executable_file "${arg_COMMAND}")
|
||||
endif()
|
||||
|
||||
set(executable_name ${arg_NAME})
|
||||
if(QT_BUILD_TESTS_BATCHED)
|
||||
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}")
|
||||
|
||||
@ -559,8 +715,8 @@ for this function. Will be ignored")
|
||||
# CROSSCOMPILING_EMULATOR don't check if actual cross compilation is configured,
|
||||
# emulator is prepended independently.
|
||||
set(crosscompiling_emulator "")
|
||||
if(CMAKE_CROSSCOMPILING AND TARGET ${arg_NAME})
|
||||
get_target_property(crosscompiling_emulator ${arg_NAME} 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()
|
||||
|
18
cmake/qbatchedtestrunner.in.cpp
Normal file
18
cmake/qbatchedtestrunner.in.cpp
Normal file
@ -0,0 +1,18 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
#include <QtTest/private/qtestcase_p.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc == 1) {
|
||||
printf("%s\n", QTest::qGetTestCaseNames().join(
|
||||
QStringLiteral(" ")).toStdString().c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto entryFunction = QTest::qGetTestCaseEntryFunction(QString::fromUtf8(argv[1]));
|
||||
return entryFunction ? entryFunction(argc - 1, argv + 1) : -1;
|
||||
}
|
@ -41,7 +41,7 @@ qt_internal_add_module(Test
|
||||
qtestaccessible.h
|
||||
qtestassert.h
|
||||
qtestblacklist.cpp qtestblacklist_p.h
|
||||
qtestcase.cpp qtestcase.h
|
||||
qtestcase.cpp qtestcase.h qtestcase_p.h
|
||||
qtestcoreelement_p.h
|
||||
qtestdata.cpp qtestdata.h
|
||||
qtestelement.cpp qtestelement_p.h
|
||||
@ -74,6 +74,7 @@ qt_internal_add_module(Test
|
||||
PRIVATE_MODULE_INTERFACE
|
||||
Qt::CorePrivate
|
||||
GENERATE_CPP_EXPORTS
|
||||
GENERATE_PRIVATE_CPP_EXPORTS
|
||||
)
|
||||
|
||||
#### Keys ignored in scope 1:.:.:testlib.pro:<TRUE>:
|
||||
@ -90,6 +91,11 @@ qt_internal_extend_target(Test CONDITION QT_FEATURE_itemmodeltester
|
||||
qabstractitemmodeltester.cpp qabstractitemmodeltester.h
|
||||
)
|
||||
|
||||
qt_internal_extend_target(Test CONDITION QT_FEATURE_batch_test_support
|
||||
SOURCES
|
||||
qtestregistry.cpp qtestregistry_p.h
|
||||
)
|
||||
|
||||
qt_internal_extend_target(Test CONDITION QT_FEATURE_valgrind
|
||||
SOURCES
|
||||
3rdparty/callgrind_p.h
|
||||
|
@ -32,6 +32,13 @@ qt_feature("valgrind" PUBLIC
|
||||
PURPOSE "Profiling support with callgrind."
|
||||
CONDITION ( LINUX OR APPLE ) AND QT_FEATURE_process AND QT_FEATURE_regularexpression
|
||||
)
|
||||
qt_feature("batch_test_support" PUBLIC
|
||||
LABEL "Batch tests"
|
||||
PURPOSE "Allows merging of all tests into a single executable on demand"
|
||||
AUTODETECT QT_BUILD_TESTS_BATCHED
|
||||
ENABLE INPUT_batch_tests STREQUAL 'yes'
|
||||
)
|
||||
qt_configure_add_summary_section(NAME "Qt Testlib")
|
||||
qt_configure_add_summary_entry(ARGS "itemmodeltester")
|
||||
qt_configure_add_summary_entry(ARGS "batch_test_support")
|
||||
qt_configure_end_summary_section() # end of "Qt Testlib" section
|
||||
|
@ -48,6 +48,8 @@ excludedirs += ../../../examples/widgets/doc
|
||||
|
||||
imagedirs += images
|
||||
|
||||
defines += QT_FEATURE_batch_test_support
|
||||
|
||||
# Add a thumbnail for examples that do not have images
|
||||
manifestmeta.thumbnail.names = "QtTestLib/Chapter *"
|
||||
|
||||
|
@ -0,0 +1 @@
|
||||
qt_commandline_option(batch-tests TYPE boolean NAME batch_tests)
|
@ -582,6 +582,7 @@ struct QtCoverageScanner
|
||||
#define TESTLIB_SELFCOVERAGE_START(name)
|
||||
#endif
|
||||
|
||||
#if !defined(QTEST_BATCH_TESTS)
|
||||
// Internal (but used by some testlib selftests to hack argc and argv).
|
||||
// Tests should normally implement initMain() if they have set-up to do before
|
||||
// instantiating the test class.
|
||||
@ -595,6 +596,30 @@ int main(int argc, char *argv[]) \
|
||||
QTEST_SET_MAIN_SOURCE_PATH \
|
||||
return QTest::qExec(&tc, argc, argv); \
|
||||
}
|
||||
#else
|
||||
// BATCHED_TEST_NAME is defined for each test in a batch in cmake. Some odd
|
||||
// targets, like snippets, don't define it though. Play safe by providing a
|
||||
// default value.
|
||||
#if !defined(BATCHED_TEST_NAME)
|
||||
#define BATCHED_TEST_NAME "other"
|
||||
#endif
|
||||
#define QTEST_MAIN_WRAPPER(TestObject, ...) \
|
||||
\
|
||||
void qRegister##TestObject() \
|
||||
{ \
|
||||
auto runTest = [](int argc, char** argv) -> int { \
|
||||
TESTLIB_SELFCOVERAGE_START(TestObject) \
|
||||
QT_PREPEND_NAMESPACE(QTest::Internal::callInitMain)<TestObject>(); \
|
||||
__VA_ARGS__ \
|
||||
TestObject tc; \
|
||||
QTEST_SET_MAIN_SOURCE_PATH \
|
||||
return QTest::qExec(&tc, argc, argv); \
|
||||
}; \
|
||||
QTest::qRegisterTestCase(BATCHED_TEST_NAME, runTest); \
|
||||
} \
|
||||
\
|
||||
Q_CONSTRUCTOR_FUNCTION(qRegister##TestObject)
|
||||
#endif
|
||||
|
||||
// For when you don't even want a QApplication:
|
||||
#define QTEST_APPLESS_MAIN(TestObject) QTEST_MAIN_WRAPPER(TestObject)
|
||||
|
@ -3,6 +3,7 @@
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
#include <QtTest/qtestcase.h>
|
||||
#include <QtTest/private/qtestcase_p.h>
|
||||
#include <QtTest/qtestassert.h>
|
||||
|
||||
#include <QtCore/qbytearray.h>
|
||||
@ -33,6 +34,9 @@
|
||||
#include <QtTest/private/qtestresult_p.h>
|
||||
#include <QtTest/private/qsignaldumper_p.h>
|
||||
#include <QtTest/private/qbenchmark_p.h>
|
||||
#if QT_CONFIG(batch_test_support)
|
||||
#include <QtTest/private/qtestregistry_p.h>
|
||||
#endif // QT_CONFIG(batch_test_support)
|
||||
#include <QtTest/private/cycle_p.h>
|
||||
#include <QtTest/private/qtestblacklist_p.h>
|
||||
#if defined(HAVE_XCTEST)
|
||||
@ -2388,6 +2392,32 @@ void QTest::qCleanup()
|
||||
#endif
|
||||
}
|
||||
|
||||
#if QT_CONFIG(batch_test_support) || defined(Q_QDOC)
|
||||
/*!
|
||||
Registers the test \a name, with entry function \a entryFunction, in a
|
||||
central test case registry for the current binary.
|
||||
|
||||
The \a name will be listed when running the batch test binary with no
|
||||
parameters. Running the test binary with the argv[1] of \a name will result
|
||||
in \a entryFunction being called.
|
||||
*/
|
||||
void QTest::qRegisterTestCase(const QString &name, TestEntryFunction entryFunction)
|
||||
{
|
||||
QTest::TestRegistry::instance()->registerTest(name, entryFunction);
|
||||
}
|
||||
|
||||
QList<QString> QTest::qGetTestCaseNames()
|
||||
{
|
||||
return QTest::TestRegistry::instance()->getAllTestNames();
|
||||
}
|
||||
|
||||
QTest::TestEntryFunction QTest::qGetTestCaseEntryFunction(const QString& name)
|
||||
{
|
||||
return QTest::TestRegistry::instance()->getTestEntryFunction(name);
|
||||
}
|
||||
|
||||
#endif // QT_CONFIG(batch_test_support)
|
||||
|
||||
/*!
|
||||
\overload
|
||||
\since 4.4
|
||||
|
@ -374,6 +374,11 @@ namespace QTest
|
||||
Q_TESTLIB_EXPORT int qExec(QObject *testObject, int argc = 0, char **argv = nullptr);
|
||||
Q_TESTLIB_EXPORT int qExec(QObject *testObject, const QStringList &arguments);
|
||||
|
||||
#if QT_CONFIG(batch_test_support) || defined(Q_QDOC)
|
||||
using TestEntryFunction = int (*)(int, char **);
|
||||
Q_TESTLIB_EXPORT void qRegisterTestCase(const QString &name, TestEntryFunction entryFunction);
|
||||
#endif // QT_CONFIG(batch_test_support)
|
||||
|
||||
Q_TESTLIB_EXPORT void setMainSourcePath(const char *file, const char *builddir = nullptr);
|
||||
|
||||
Q_TESTLIB_EXPORT bool qVerify(bool statement, const char *statementStr, const char *description,
|
||||
|
36
src/testlib/qtestcase_p.h
Normal file
36
src/testlib/qtestcase_p.h
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
#ifndef QTESTCASE_P_H
|
||||
#define QTESTCASE_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include <QtTest/qtestcase.h>
|
||||
#include <QtTest/private/qttestexports_p.h>
|
||||
#include <QtTest/qttestglobal.h>
|
||||
|
||||
#include <QtCore/qstring.h>
|
||||
#include <QtCore/qnamespace.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace QTest {
|
||||
#if QT_CONFIG(batch_test_support)
|
||||
Q_TESTLIB_PRIVATE_EXPORT QList<QString> qGetTestCaseNames();
|
||||
Q_TESTLIB_PRIVATE_EXPORT TestEntryFunction qGetTestCaseEntryFunction(const QString &name);
|
||||
#endif // QT_CONFIG(batch_test_support)
|
||||
} // namespace QTest
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QTESTCASE_P_H
|
36
src/testlib/qtestregistry.cpp
Normal file
36
src/testlib/qtestregistry.cpp
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
#include <QtTest/private/qtestregistry_p.h>
|
||||
|
||||
QT_REQUIRE_CONFIG(batch_test_support);
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace QTest {
|
||||
Q_GLOBAL_STATIC(TestRegistry, g_registry);
|
||||
|
||||
TestRegistry *TestRegistry::instance()
|
||||
{
|
||||
return g_registry;
|
||||
}
|
||||
|
||||
void TestRegistry::registerTest(const QString& name, TestEntryFunction entry)
|
||||
{
|
||||
m_tests.emplace(name, std::move(entry));
|
||||
}
|
||||
|
||||
TestRegistry::TestEntryFunction
|
||||
TestRegistry::getTestEntryFunction(const QString& name) const
|
||||
{
|
||||
const auto it = m_tests.find(name);
|
||||
return it != m_tests.end() ? it.value() : nullptr;
|
||||
}
|
||||
|
||||
QStringList TestRegistry::getAllTestNames() const
|
||||
{
|
||||
return m_tests.keys();
|
||||
}
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
36
src/testlib/qtestregistry_p.h
Normal file
36
src/testlib/qtestregistry_p.h
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
#ifndef QTESTREGISTRY_P_H
|
||||
#define QTESTREGISTRY_P_H
|
||||
|
||||
#include <QString>
|
||||
#include <QtCore/qhash.h>
|
||||
#include <QtTest/qttestglobal.h>
|
||||
|
||||
QT_REQUIRE_CONFIG(batch_test_support);
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace QTest {
|
||||
class TestRegistry {
|
||||
public:
|
||||
using TestEntryFunction = int(*)(int argv, char** argc);
|
||||
|
||||
static TestRegistry* instance();
|
||||
|
||||
void registerTest(const QString& name, TestEntryFunction data);
|
||||
size_t total() const {
|
||||
return m_tests.size();
|
||||
}
|
||||
TestEntryFunction getTestEntryFunction(const QString& name) const;
|
||||
QStringList getAllTestNames() const;
|
||||
|
||||
private:
|
||||
QHash<QString, TestEntryFunction> m_tests;
|
||||
};
|
||||
} // namespace QTest
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QTESTREGISTRY_P_H
|
Loading…
Reference in New Issue
Block a user