1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
|
# SPDX-FileCopyrightText: 2013 Alexander Richardson <arichardson.kde@gmail.com>
# SPDX-FileCopyrightText: 2015 Alex Merry <alex.merry@kde.org>
# SPDX-FileCopyrightText: 2025 Friedrich W. H. Kossebau <kossebau@kde.org>
#
# SPDX-License-Identifier: BSD-3-Clause
#[=======================================================================[.rst:
ECMAddTests
-----------
Convenience functions for adding tests.
::
ecm_add_tests(<sources>
[COMPILE_DEFINITIONS <definition> [<definition> [...]]] # Since 6.13.0
[ENVIRONMENT <list>] # Since 6.13.0
LINK_LIBRARIES <library> [<library> [...]]
[NAME_PREFIX <prefix> (| NO_NAME_PREFIX] # Since 6.22.0)
[GUI]
[TARGET_NAMES_VAR <target_names_var>]
[TEST_NAMES_VAR <test_names_var>]
[WORKING_DIRECTORY <dir>] # Since 5.111
)
A convenience function for adding multiple tests, each consisting of a
single source file. For each file in <sources>, an executable target is
created (whose name is the base name of the source file) with the compiler
definitions passed with ``COMPILE_DEFINITIONS``. This will be linked
against the libraries given with ``LINK_LIBRARIES``. Each executable will
be added as a test with the same name and can have an environment provided
by ``ENVIRONMENT``.
If ``NAME_PREFIX`` is given, this prefix will be prepended to the test names, but
not the target names. As a result, it will not prevent clashes between tests
with the same name in different parts of the project, but it can be used to
give an indication of where to look for a failing test.
If ``NAME_PREFIX`` is not set, it will default to a value depending on the
strategy as controlled by the variable ``ECM_TEST_NAME_PREFIX_STRATEGY``, if
set in the current scope and ``NO_NAME_PREFIX`` is not set (since 6.22).
If the flag ``GUI`` is passed the test binaries will be GUI executables, otherwise
the resulting binaries will be console applications (regardless of the value
of ``CMAKE_WIN32_EXECUTABLE`` or ``CMAKE_MACOSX_BUNDLE``). Be aware that this changes
the executable entry point on Windows (although some frameworks, such as Qt,
abstract this difference away).
The tests will be build with ``-DQT_FORCE_ASSERTS`` to enable assertions in the
test executable even for release builds.
The ``TARGET_NAMES_VAR`` and ``TEST_NAMES_VAR`` arguments, if given, should specify a
variable name to receive the list of generated target and test names,
respectively. This makes it convenient to apply properties to them as a
whole, for example, using ``set_target_properties()`` or ``set_tests_properties()``.
The generated target executables will have the effects of ``ecm_mark_as_test()``
(from the :module:`ECMMarkAsTest` module) applied to it.
``WORKING_DIRECTORY`` sets the test property `WORKING_DIRECTORY
<https://cmake.org/cmake/help/latest/prop_test/WORKING_DIRECTORY.html>`_
in which to execute the test. By default the test will be run in
``${CMAKE_CURRENT_BINARY_DIR}``. The working directory can be specified using
generator expressions. Since 5.111.
::
ecm_add_test(
<sources>
[COMPILE_DEFINITIONS <definition> [<definition> [...]]] # Since 6.13.0
[ENVIRONMENT <list>] # Since 6.13.0
LINK_LIBRARIES <library> [<library> [...]]
[TEST_NAME <name>]
[NAME_PREFIX <prefix> (| NO_NAME_PREFIX] # Since 6.22.0)
[GUI]
[TARGET_NAME_VAR <target_name_var>]
[TEST_NAME_VAR <test_name_var>]
[WORKING_DIRECTORY <dir>] # Since 5.111
)
This is a single-test form of ``ecm_add_tests`` that allows multiple source files
to be used for a single test. If using multiple source files, ``TEST_NAME`` must
be given; this will be used for both the target and test names.
As with ``ecm_add_tests()``, the ``NAME_PREFIX`` argument will be prepended to
the test name.
If ``NAME_PREFIX`` is not set, it will default to a value depending on the
strategy as controlled by the variable ``ECM_TEST_NAME_PREFIX_STRATEGY``, if
set in the current scope and ``NO_NAME_PREFIX`` is not set (since 6.22).
The ``TARGET_NAME_VAR`` and ``TEST_NAME_VAR`` arguments, if given, should specify a
variable name to receive the generated target and test name,
respectively. This makes it convenient to apply properties to them as a
whole, for example, using ``set_target_properties()`` or ``set_tests_properties()``.
``WORKING_DIRECTORY`` sets the test property `WORKING_DIRECTORY
<https://cmake.org/cmake/help/latest/prop_test/WORKING_DIRECTORY.html>`_
in which to execute the test. By default the test will be run in
``${CMAKE_CURRENT_BINARY_DIR}``. The working directory can be specified using
generator expressions. Since 5.111.
::
ecm_test_set_dir_properties( # Since 6.22.0
[DIR <dir>]
[PREFIX_NAME <name>]
[PREFIX_NAME_IGNORE | PREFIX_NAME_NOT_IGNORE]
[FIRST_PREFIX_NAME]
[LAST_PREFIX_NAME]
)
The function can be used to set properties to individual directories to
influence the behaviour of the test functions. The properties will be set to
the current source directory. For source directories which themselves do not
have a CMakeLists.txt file and thus are not added by any `add_subdirectory()
<https://cmake.org/cmake/help/latest/command/add_subdirectory.html>`_ calls,
the ``DIR`` argument can be used to instead set the property to any such
subdirectory ``<dir>``.
``PREFIX_NAME`` can be used to override by ``<name>`` the name derived from
the given directory.
``PREFIX_NAME_IGNORE`` or ``PREFIX_NAME_NOT_IGNORE`` can be used to override
the filtering done by ``ECM_TEST_NAME_PREFIX_IGNORE_DIRS`` for the name of the
given directory.
``FIRST_PREFIX_NAME`` and ``LAST_PREFIX_NAME`` can be used to limit the range
of directories in the path used for deriving the prefix name.
::
ecm_test_get_name_prefix( # Since 6.22.0
<name_prefix_var>
)
The ``name_prefix_var`` argument should specify a variable name to receive the
default test prefix name for the current scope, based on
the value of ``ECM_TEST_NAME_PREFIX_STRATEGY`` and the respective further setup.
::
ECM_TEST_NAME_PREFIX_STRATEGY # Since 6.22.0
This variable is specifying the strategy to use when estimating the default
test name prefix to use by the macros ``ecm_add_tests()`` and ``ecm_add_test()``
when no explicit ``NAME_PREFIX`` argument is passed. The supported values are:
* ``VARIABLE``: The default name prefix is taken from the value of the variable
``ECM_TEST_NAME_PREFIX`` in the scope where the macros are called.
* ``PATH``: The default name prefix is derived from the relative path of the
current source directory where the macros are called, by replacing the
dir separators with "-".
See also ``ECM_TEST_NAME_PREFIX_IGNORE_DIRS`` for skipping some directory names.
When unset, the ``VARIABLE`` strategy is used.
::
ECM_TEST_NAME_PREFIX # Since 6.22.0
When ``ECM_TEST_NAME_PREFIX_STRATEGY`` is set to ``VARIABLE``,
the current value of the variable is used as default for the test name prefix.
::
ECM_TEST_NAME_PREFIX_IGNORE_DIRS # Since 6.22.0
When ``ECM_TEST_NAME_PREFIX_STRATEGY`` is set to ``PATH``, this variable is used
to filter out directory names in the relative path of the current source directory
when deriving the name prefix. By default the variable is set to
"src", "test", "tests", "autotest", "autotests" when including the ``ECMAddTest`` module,
if not already defined.
Example usage:
.. code-block:: cmake
set(ECM_TEST_NAME_PREFIX_STRATEGY "PATH")
list(APPEND ECM_TEST_NAME_PREFIX_IGNORE_DIRS "plugins")
# Being in subdir "src/plugins/foo" this test will get
# the test name prefix set to "foo-", next to base name "mytest":
ecm_add_test(
mytest.cpp
LINK_LIBRARIES mylib
)
Since pre-1.0.0.
#]=======================================================================]
cmake_policy(VERSION 3.16)
include(ECMMarkAsTest)
include(ECMMarkNonGuiExecutable)
# set variable on the include scope, as consumers are supposed to have access to it
if(NOT DEFINED ECM_TEST_NAME_PREFIX_IGNORE_DIRS)
set(ECM_TEST_NAME_PREFIX_IGNORE_DIRS "src" "test" "tests" "autotest" "autotests")
endif()
function(_ecm_test_name_prefix_from_project_path _name_prefix_varname)
# estimate names of chain of dirs of current source dir to main dir
file(RELATIVE_PATH _project_path ${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
if(_project_path MATCHES "^\\.\\./")
message(FATAL_ERROR "The PATH strategy can not be used with directories outside the root source dir.")
endif()
string(REPLACE "/" ";" _dirs ${_project_path})
# derive prefix names
set(_names)
set(_subpath ${CMAKE_SOURCE_DIR})
set(_lastcmakesubpath ${_subpath}) # path of current including CMakeLists.txt
set(_relativelastcmakesubpath)
get_directory_property(_cmakesubdirs DIRECTORY ${_lastcmakesubpath} "SUBDIRECTORIES")
foreach(_dir ${_dirs})
string(APPEND _subpath "/${_dir}")
# subdir with CMakeLists.txt?
if(_subpath IN_LIST _cmakesubdirs)
set(_lastcmakesubpath ${_subpath})
set(_relativelastcmakesubpath)
set(_property_name_suffix)
# prepare nexs subdir
get_directory_property(_cmakesubdirs DIRECTORY ${_lastcmakesubpath} "SUBDIRECTORIES")
else()
string(APPEND _relativelastcmakesubpath "/${_dir}")
# keep in sync with writing the properties in ecm_test_set_dir_properties
string(SHA1 _relativelastcmakesubpath_hash ${_relativelastcmakesubpath})
set(_property_name_suffix ":${_relativelastcmakesubpath_hash}")
endif()
get_directory_property(_substitute_name DIRECTORY ${_lastcmakesubpath} "ECM_TEST_NAME_PREFIX_DIR_NAME${_property_name_suffix}")
get_directory_property(_first_name DIRECTORY ${_lastcmakesubpath} "ECM_TEST_NAME_PREFIX_DIR_FIRST${_property_name_suffix}")
if(_first_name)
# drop any current names
set(_names)
endif()
if (_substitute_name)
list(APPEND _names ${_substitute_name})
else()
get_directory_property(_ignore_dir DIRECTORY ${_lastcmakesubpath} "ECM_TEST_NAME_PREFIX_DIR_IGNORE${_property_name_suffix}")
if (_ignore_dir STREQUAL "")
if(_dir IN_LIST ECM_TEST_NAME_PREFIX_IGNORE_DIRS)
set(_ignore_dir TRUE)
else()
set(_ignore_dir FALSE)
endif()
endif()
if(NOT _ignore_dir)
list(APPEND _names ${_dir})
endif()
endif()
get_directory_property(_last_name DIRECTORY ${_lastcmakesubpath} "ECM_TEST_NAME_PREFIX_DIR_LAST${_property_name_suffix}")
if(_last_name)
# finish estimating prefix names
break()
endif()
endforeach()
# create final prefix name
string(JOIN "-" _name_prefix ${_names})
if (_name_prefix)
string(APPEND _name_prefix "-")
endif()
set(${_name_prefix_varname} ${_name_prefix} PARENT_SCOPE)
endfunction()
function(ecm_test_set_dir_properties)
set(options PREFIX_NAME_IGNORE PREFIX_NAME_NOT_IGNORE FIRST_PREFIX_NAME LAST_PREFIX_NAME)
set(oneValueArgs PREFIX_NAME DIR)
set(multiValueArgs)
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(ARG_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "Unknown keywords given to ECM_TEST_SET_DIR_PROPERTIES(): \"${ARG_UNPARSED_ARGUMENTS}\"")
endif()
if(ARG_PREFIX_NAME_IGNORE AND ARG_PREFIX_NAME_NOT_IGNORE)
message(FATAL_ERROR "Pass either PREFIX_NAME_IGNORE or PREFIX_NAME_NOT_IGNORE to ECM_TEST_SET_DIR_PROPERTIES.")
endif()
set(_subdir)
if(ARG_DIR)
get_filename_component(_subdir ${ARG_DIR} REALPATH)
if(NOT IS_DIRECTORY "${_subdir}")
message(FATAL_ERROR "DIR argument passed to ECM_TEST_SET_DIR_PROPERTIES is not a directory: \"${ARG_DIR}\"")
endif()
if (_subdir STREQUAL ${CMAKE_CURRENT_SOURCE_DIR})
set(_subdir)
else()
file(RELATIVE_PATH _subdir ${CMAKE_CURRENT_SOURCE_DIR} ${_subdir})
if(_subdir MATCHES "^\\.\\./")
message(FATAL_ERROR "DIR argument passed to ECM_TEST_SET_DIR_PROPERTIES is not a subdirectory: \"${ARG_DIR}\"")
endif()
endif()
endif()
if(_subdir)
# map to a hash, as cmake does not specify allowed property characters
# keep in sync with reading the properties in _ecm_test_name_prefix_from_project_path
string(SHA1 _subdir_hash "/${_subdir}")
set(_property_name_suffix ":${_subdir_hash}")
else()
set(_property_name_suffix)
endif()
if (ARG_PREFIX_NAME)
set_directory_properties(PROPERTIES "ECM_TEST_NAME_PREFIX_DIR_NAME${_property_name_suffix}" "${ARG_PREFIX_NAME}")
endif()
if (ARG_PREFIX_NAME_IGNORE)
set_directory_properties(PROPERTIES "ECM_TEST_NAME_PREFIX_DIR_IGNORE${_property_name_suffix}" "TRUE")
endif()
if (ARG_PREFIX_NAME_NOT_IGNORE)
set_directory_properties(PROPERTIES "ECM_TEST_NAME_PREFIX_DIR_IGNORE${_property_name_suffix}" "FALSE")
endif()
if (ARG_FIRST_PREFIX_NAME)
set_directory_properties(PROPERTIES "ECM_TEST_NAME_PREFIX_DIR_FIRST${_property_name_suffix}" "TRUE")
endif()
if (ARG_LAST_PREFIX_NAME)
set_directory_properties(PROPERTIES "ECM_TEST_NAME_PREFIX_DIR_LAST${_property_name_suffix}" "TRUE")
endif()
endfunction()
function(ecm_test_get_name_prefix _name_prefix_varname)
if(NOT ECM_TEST_NAME_PREFIX_STRATEGY)
set(_strategy "VARIABLE")
else()
set(_strategies "VARIABLE" "PATH")
if (NOT ECM_TEST_NAME_PREFIX_STRATEGY IN_LIST _strategies)
message(FATAL_ERROR "ECM_TEST_NAME_PREFIX_STRATEGY set to unknown value ${ECM_TEST_NAME_PREFIX_STRATEGY}")
endif()
set(_strategy ${ECM_TEST_NAME_PREFIX_STRATEGY})
endif()
if (${_strategy} STREQUAL "PATH")
_ecm_test_name_prefix_from_project_path(_name_prefix)
else()
set(_name_prefix ${ECM_TEST_NAME_PREFIX})
endif()
set(${_name_prefix_varname} ${_name_prefix} PARENT_SCOPE)
endfunction()
function(ecm_add_test)
set(options GUI NO_NAME_PREFIX)
set(oneValueArgs TEST_NAME NAME_PREFIX TARGET_NAME_VAR TEST_NAME_VAR WORKING_DIRECTORY)
set(multiValueArgs COMPILE_DEFINITIONS ENVIRONMENT LINK_LIBRARIES)
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
set(_sources ${ARG_UNPARSED_ARGUMENTS})
list(LENGTH _sources _sourceCount)
if(ARG_TEST_NAME)
set(_targetname ${ARG_TEST_NAME})
elseif(${_sourceCount} EQUAL "1")
#use the source file name without extension as the testname
get_filename_component(_targetname ${_sources} NAME_WE)
else()
#more than one source file passed, but no test name given -> error
message(FATAL_ERROR "ecm_add_test() called with multiple source files but without setting \"TEST_NAME\"")
endif()
if (NOT ARG_NAME_PREFIX)
if(NOT ARG_NO_NAME_PREFIX)
ecm_test_get_name_prefix(ARG_NAME_PREFIX)
endif()
else()
if(ARG_NO_NAME_PREFIX)
message(FATAL_ERROR "ecm_add_test() called with both \"NO_NAME_PREFIX\" and \"NAME_PREFIX\"")
endif()
endif()
set(_testname ${ARG_NAME_PREFIX}${_targetname})
set(gui_args)
if(ARG_GUI)
set(gui_args WIN32 MACOSX_BUNDLE)
endif()
add_executable(${_targetname} ${gui_args} ${_sources})
if(NOT ARG_GUI)
ecm_mark_nongui_executable(${_targetname})
endif()
set(test_args)
if(DEFINED ARG_WORKING_DIRECTORY)
list(APPEND test_args WORKING_DIRECTORY ${ARG_WORKING_DIRECTORY})
endif()
add_test(NAME ${_testname} COMMAND ${_targetname} ${test_args})
target_link_libraries(${_targetname} ${ARG_LINK_LIBRARIES})
target_compile_definitions(${_targetname} PRIVATE -DQT_FORCE_ASSERTS ${ARG_COMPILE_DEFINITIONS})
ecm_mark_as_test(${_targetname})
if (CMAKE_LIBRARY_OUTPUT_DIRECTORY)
set(_plugin_path ${CMAKE_LIBRARY_OUTPUT_DIRECTORY})
if (DEFINED ENV{QT_PLUGIN_PATH})
if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
# https://stackoverflow.com/questions/59862894/how-do-i-make-a-list-in-cmake-with-the-semicolon-value
set(PATHSEP "\\\;") # Don't want cmake to treat it like a list
else() # e.g. Linux
set(PATHSEP ":")
endif()
set(_plugin_path "${_plugin_path}${PATHSEP}$ENV{QT_PLUGIN_PATH}")
endif()
list(APPEND ARG_ENVIRONMENT "QT_PLUGIN_PATH=${_plugin_path}")
endif()
if (ARG_ENVIRONMENT)
list(JOIN ARG_ENVIRONMENT ";" env)
set_property(TEST ${_testname} PROPERTY ENVIRONMENT "${env}")
endif()
if (ARG_TARGET_NAME_VAR)
set(${ARG_TARGET_NAME_VAR} "${_targetname}" PARENT_SCOPE)
endif()
if (ARG_TEST_NAME_VAR)
set(${ARG_TEST_NAME_VAR} "${_testname}" PARENT_SCOPE)
endif()
endfunction()
function(ecm_add_tests)
set(options GUI NO_NAME_PREFIX)
set(oneValueArgs NAME_PREFIX TARGET_NAMES_VAR TEST_NAMES_VAR WORKING_DIRECTORY)
set(multiValueArgs COMPILE_DEFINITIONS ENVIRONMENT LINK_LIBRARIES)
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
set(_name_prefix "")
if(ARG_NO_NAME_PREFIX)
if(ARG_NAME_PREFIX)
message(FATAL_ERROR "ecm_add_tests() called with both \"NO_NAME_PREFIX\" and \"NAME_PREFIX\"")
endif()
elseif(ARG_NAME_PREFIX)
set(_name_prefix "NAME_PREFIX" "${ARG_NAME_PREFIX}")
endif()
if(ARG_GUI)
set(_exe_type GUI)
else()
set(_exe_type "")
endif()
set(test_args)
if(DEFINED ARG_WORKING_DIRECTORY)
list(APPEND test_args WORKING_DIRECTORY ${ARG_WORKING_DIRECTORY})
endif()
set(test_names)
set(target_names)
foreach(_test_source ${ARG_UNPARSED_ARGUMENTS})
ecm_add_test(${_test_source}
${_name_prefix}
COMPILE_DEFINITIONS ${ARG_COMPILE_DEFINITIONS}
ENVIRONMENT ${ARG_ENVIRONMENT}
LINK_LIBRARIES ${ARG_LINK_LIBRARIES}
TARGET_NAME_VAR target_name
TEST_NAME_VAR test_name
${_exe_type}
${test_args}
)
list(APPEND _test_names "${test_name}")
list(APPEND _target_names "${target_name}")
endforeach()
if (ARG_TARGET_NAMES_VAR)
set(${ARG_TARGET_NAMES_VAR} "${_target_names}" PARENT_SCOPE)
endif()
if (ARG_TEST_NAMES_VAR)
set(${ARG_TEST_NAMES_VAR} "${_test_names}" PARENT_SCOPE)
endif()
endfunction()
|