1# ============================================================================
2# Copyright (c) 2011-2012 University of Pennsylvania
3# Copyright (c) 2013-2016 Andreas Schuh
4# All rights reserved.
5#
6# See COPYING file for license information or visit
7# https://cmake-basis.github.io/download.html#license
8# ============================================================================
9
10##############################################################################
11# @file  BasisTest.cmake
12# @brief CTest configuration. Include this module instead of CTest.
13#
14# @note This module is included by basis_project_initialize().
15#
16# @ingroup CMakeAPI
17##############################################################################
18
19# ============================================================================
20# configuration
21# ============================================================================
22
23## @brief Request build of tests.
24option (BUILD_TESTING "Request build of tests." OFF)
25
26# include CTest module which enables testing, but prevent it from generating
27# any configuration file or adding targets yet as we want to adjust the
28# default CTest settings--in particular the site name--before
29set (RUN_FROM_DART 1)
30include (CTest)
31
32# mark timeout option as advanced
33mark_as_advanced (DART_TESTING_TIMEOUT)
34
35set (RUN_FROM_CTEST_OR_DART 1)
36include (CTestTargets)
37set (RUN_FROM_CTEST_OR_DART)
38
39# custom CTest configuration
40if (EXISTS "${PROJECT_CONFIG_DIR}/CTestCustom.cmake.in")
41  configure_file (
42    "${PROJECT_CONFIG_DIR}/CTestCustom.cmake.in"
43    "${PROJECT_BINARY_DIR}/CTestCustom.cmake"
44    @ONLY
45  )
46elseif (EXISTS "${PROJECT_CONFIG_DIR}/CTestCustom.cmake")
47  configure_file (
48    "${PROJECT_CONFIG_DIR}/CTestCustom.cmake"
49    "${PROJECT_BINARY_DIR}/CTestCustom.cmake"
50    COPYONLY
51  )
52else ()
53  basis_get_relative_path (CONFIG_DIR "${PROJECT_SOURCE_DIR}" "${PROJECT_CONFIG_DIR}")
54  if (EXISTS "${BASIS_TEMPLATE_DIR}/${CONFIG_DIR}/CTestCustom.cmake.in")
55    # to avoid a Doxygen warning, we need to replace certain patterns used by
56    # the basisproject tool to replace them with project related information
57    #
58    # Note: Do this only on the first pass, otherwise configure_file() will
59    #       retrigger CMake every time b/c the modification timestamp of the
60    #       source file is newer than the previously configured file.
61    #       The use of the "cmake -E copy_if_different" command could be used
62    #       here instead, but as we do not expect the CTestCustom.cmake.in
63    #       file of the BASIS project template to change, it is sufficient
64    #       to only check if we copied the template file already.
65    if (NOT EXISTS "${PROJECT_BINARY_DIR}/CTestCustom.cmake.in")
66      file (READ "${BASIS_TEMPLATE_DIR}/${CONFIG_DIR}/CTestCustom.cmake.in" _TEMPLATE)
67      string (REGEX REPLACE "<year>" "" _TEMPLATE "${_TEMPLATE}")
68      file (WRITE "${PROJECT_BINARY_DIR}/CTestCustom.cmake.in" "${_TEMPLATE}")
69      unset (_TEMPLATE)
70    endif ()
71    # configure the modified template file
72    configure_file (
73      "${PROJECT_BINARY_DIR}/CTestCustom.cmake.in"
74      "${PROJECT_BINARY_DIR}/CTestCustom.cmake"
75      @ONLY
76    )
77  endif ()
78endif ()
79
80# ============================================================================
81# constants
82# ============================================================================
83
84## @brief Names of recognized properties on tests.
85#
86# Unfortunately, the @c ARGV and @c ARGN arguments of a CMake function()
87# or macro() does not preserve values which themselves are lists. Therefore,
88# it is not possible to distinguish between property names and their values
89# in the arguments passed to basis_set_tests_properties().
90# To overcome this problem, this list specifies all the possible property names.
91# Everything else is considered to be a property value except the first argument
92# follwing right after the @c PROPERTIES keyword. Alternatively,
93# basis_set_property() can be used as here no disambiguity exists.
94#
95# @note Placeholders such as &lt;CONFIG&gt; are allowed. These are treated
96#       as the regular expression "[^ ]+". See basis_list_to_regex().
97#
98# @sa http://www.cmake.org/cmake/help/cmake-2-8-docs.html#section_PropertiesonTests
99set (BASIS_PROPERTIES_ON_TESTS
100  ATTACHED_FILES
101  ATTACHED_FILES_ON_FAIL
102  COST
103  DEPENDS
104  ENVIRONMENT
105  FAIL_REGULAR_EXPRESSION
106  LABELS
107  MEASUREMENT
108  PASS_REGULAR_EXPRESSION
109  PROCESSORS
110  REQUIRED_FILES
111  RESOURCE_LOCK
112  RUN_SERIAL
113  TIMEOUT
114  WILL_FAIL
115  WORKING_DIRECTORY
116)
117
118# convert list into regular expression
119basis_list_to_regex (BASIS_PROPERTIES_ON_TESTS_RE ${BASIS_PROPERTIES_ON_TESTS})
120
121# ============================================================================
122# utilities
123# ============================================================================
124
125## @addtogroup CMakeUtilities
126#  @{
127
128
129# ----------------------------------------------------------------------------
130## @brief Disable testing if project does not implement any tests.
131#
132# This function checks if there are test/ subdirectories in the project and
133# disables and hides the BUILD_TESTING option if none are found.
134function (basis_disable_testing_if_no_tests)
135  if (IS_DIRECTORY "${PROJECT_TESTING_DIR}")
136    set (DISABLE_TESTING FALSE)
137  else ()
138    set (DISABLE_TESTING TRUE)
139  endif ()
140  if (DISABLE_TESTING)
141    basis_get_relative_path (TESTING_DIR "${PROJECT_SOURCE_DIR}" "${PROJECT_TESTING_DIR}")
142    foreach (M IN LISTS PROJECT_MODULES_ENABLED)
143      if (IS_DIRECTORY "${MODULE_${M}_SOURCE_DIR}/${TESTING_DIR}")
144        set (DISABLE_TESTING FALSE)
145        break ()
146      endif ()
147    endforeach ()
148  endif ()
149  if (DISABLE_TESTING)
150    set_property (CACHE BUILD_TESTING PROPERTY VALUE OFF)
151    set_property (CACHE BUILD_TESTING PROPERTY TYPE  INTERNAL)
152  else ()
153    set_property (CACHE BUILD_TESTING PROPERTY TYPE BOOL)
154  endif ()
155endfunction ()
156
157# ----------------------------------------------------------------------------
158## @brief Set a property of the tests.
159#
160# This function replaces CMake's
161# <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:set_tests_properties">
162# set_tests_properties()</a> command.
163#
164# @note Due to a bug in CMake (http://www.cmake.org/Bug/view.php?id=12303),
165#       except of the first property given directly after the @c PROPERTIES keyword,
166#       only properties listed in @c BASIS_PROPERTIES_ON_TESTS can be set.
167#
168# @param [in] ARGN List of arguments for
169#                  <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:set_tests_properties">
170#                  set_tests_properties()</a>.
171#
172# @returns Sets the given properties of the specified test.
173#
174# @sa http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:set_tests_properties
175#
176# @ingroup CMakeAPI
177function (basis_set_tests_properties)
178  # convert test names to UIDs
179  set (TEST_UIDS)
180  list (GET ARGN 0 ARG)
181  while (ARG AND NOT ARG MATCHES "^PROPERTIES$")
182    basis_get_test_uid (TEST_UID "${ARG}")
183    list (APPEND TEST_UIDS "${TEST_UID}")
184    list (REMOVE_AT ARGN 0)
185    list (GET ARGN 0 ARG)
186  endwhile ()
187  if (NOT ARG MATCHES "^PROPERTIES$")
188    message (FATAL_ERROR "Missing PROPERTIES argument!")
189  elseif (NOT TEST_UIDS)
190    message (FATAL_ERROR "No test specified!")
191  endif ()
192  # remove PROPERTIES keyword
193  list (REMOVE_AT ARGN 0)
194  # set tests properties
195  #
196  # Note: By iterating over the properties, the empty property values
197  #       are correctly passed on to CMake's set_tests_properties()
198  #       command, while
199  #       set_tests_properties(${TEST_UIDS} PROPERTIES ${ARGN})
200  #       (erroneously) discards the empty elements in ARGN.
201  if (BASIS_DEBUG)
202    message ("** basis_set_tests_properties:")
203    message ("**   Test(s)  :  ${TEST_UIDS}")
204    message ("**   Properties: [${ARGN}]")
205  endif ()
206  list (LENGTH ARGN N)
207  while (N GREATER 1)
208    list (GET ARGN 0 PROPERTY)
209    list (GET ARGN 1 VALUE)
210    list (REMOVE_AT ARGN 0 1)
211    list (LENGTH ARGN N)
212    # The following loop is only required b/c CMake's ARGV and ARGN
213    # lists do not support arguments which are themselves lists.
214    # Therefore, we need a way to decide when the list of values for a
215    # property is terminated. Hence, we only allow known properties
216    # to be set, except for the first property where the name follows
217    # directly after the PROPERTIES keyword.
218    while (N GREATER 0)
219      list (GET ARGN 0 ARG)
220      if (ARG MATCHES "${BASIS_PROPERTIES_ON_TESTS_RE}")
221        break ()
222      endif ()
223      list (APPEND VALUE "${ARG}")
224      list (REMOVE_AT ARGN 0)
225      list (LENGTH ARGN N)
226    endwhile ()
227    # check property name
228    if ("${PROPERTY}" STREQUAL "")
229      message (FATAL_ERROR "Empty property name given!")
230    endif ()
231    # map test names to test UIDs
232    if (PROPERTY MATCHES "^DEPENDS$")
233      set (UIDS)
234      foreach (V IN LISTS VALUE)
235        basis_get_test_uid (UID "${V}")
236        list (APPEND UIDS "${UID}")
237      endforeach ()
238      set (VALUE ${UIDS})
239    endif ()
240    # set target property
241    if (BASIS_DEBUG)
242      message ("**   -> ${PROPERTY} = [${VALUE}]")
243    endif ()
244    set_tests_properties (${TEST_UIDS} PROPERTIES ${PROPERTY} "${VALUE}")
245  endwhile ()
246  # make sure that every property had a corresponding value
247  if (NOT N EQUAL 0)
248    message (FATAL_ERROR "No value given for test property ${ARGN}")
249  endif ()
250endfunction ()
251
252# ----------------------------------------------------------------------------
253## @brief Get a property of the test.
254#
255# This function replaces CMake's
256# <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:get_test_property">
257# get_test_property()</a> command.
258#
259# @param [out] VAR       Property value.
260# @param [in]  TEST_NAME Name of test.
261# @param [in]  ARGN      Remaining arguments of
262#                        <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:get_test_property">
263#                        get_test_property()</a>.
264#
265# @returns Sets @p VAR to the value of the requested property.
266#
267# @sa http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:get_test_property
268#
269# @ingroup CMakeAPI
270function (basis_get_test_property VAR TEST_NAME)
271  basis_get_test_uid (TEST_UID "${TEST_NAME}")
272  get_test_property (VALUE "${TEST_UID}" ${ARGN})
273  set (${VAR} "${VALUE}" PARENT_SCOPE)
274endfunction ()
275
276# ----------------------------------------------------------------------------
277## @brief Create and add a test driver executable.
278#
279# @param [in] TESTDRIVER_NAME Name of the test driver.
280# @param [in] ARGN            List of source files implementing tests.
281#
282# @ingroup CMakeAPI
283function (basis_add_test_driver TESTDRIVER_NAME)
284  # parse arguments
285  CMAKE_PARSE_ARGUMENTS (
286    ARGN
287      ""
288      "EXTRA_INCLUDE;FUNCTION"
289      ""
290    ${ARGN}
291  )
292  if (ARGN_EXTRA_INCLUDE OR ARGN_FUNCTION)
293    message (FATAL_ERROR "Invalid argument EXTRA_INCLUDE or FUNCTION! "
294                         "Use create_test_sourcelist() if you want to create "
295                         "a custom test driver.")
296  endif ()
297  # choose test driver implementation depending on which packages are available
298  set (TESTDRIVER_INCLUDE      "basis/testdriver.h")
299  set (TESTDRIVER_LINK_DEPENDS basis.basis)
300  if (ITK_FOUND)
301    basis_include_directories (BEFORE ${ITK_INCLUDE_DIRS})
302    list (APPEND TESTDRIVER_LINK_DEPENDS ${ITK_LIBRARIES})
303  endif ()
304  if (WIN32)
305    list (APPEND TESTDRIVER_LINK_DEPENDS Ws2_32)
306  endif ()
307  # create test driver source code
308  set (TESTDRIVER_SOURCE "${TESTDRIVER_NAME}")
309  if (NOT TESTDRIVER_SOURCE MATCHES "\\.cxx")
310    set (TESTDRIVER_SOURCE "${TESTDRIVER_SOURCE}.cxx")
311  endif ()
312  set (CMAKE_TESTDRIVER_BEFORE_TESTMAIN "    #include <basis/testdriver-before-test.inc>")
313  set (CMAKE_TESTDRIVER_AFTER_TESTMAIN  "    #include <basis/testdriver-after-test.inc>")
314  create_test_sourcelist (
315    TESTDRIVER_SOURCES
316      ${TESTDRIVER_SOURCE} ${ARGN_UNPARSED_ARGUMENTS}
317      EXTRA_INCLUDE "${TESTDRIVER_INCLUDE}"
318      FUNCTION      testdriversetup
319  )
320  # add test driver executable
321  basis_add_executable (${TESTDRIVER_NAME} ${TESTDRIVER_SOURCES})
322  basis_target_link_libraries (${TESTDRIVER_NAME} ${TESTDRIVER_LINK_DEPENDS})
323  if (ITK_FOUND)
324    basis_set_target_properties (
325      ${TESTDRIVER_NAME}
326      PROPERTIES
327        COMPILE_DEFINITIONS
328          TESTDRIVER_NAME=\"${TESTDRIVER_NAME}\"
329          ITK_VERSION=\"${ITK_VERSION_MAJOR}.${ITK_VERSION_MINOR}.${ITK_VERSION_PATCH}\"
330          ITK_VERSION_MAJOR=${ITK_VERSION_MAJOR}
331    )
332  else ()
333    basis_set_target_properties (
334      ${TESTDRIVER_NAME}
335      PROPERTIES
336        COMPILE_DEFINITIONS
337          TESTDRIVER_NAME=\"${TESTDRIVER_NAME}\"
338    )
339  endif ()
340endfunction ()
341
342# ----------------------------------------------------------------------------
343## @brief Add test.
344#
345# This command is used similar to CMake's
346# <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:add_test">
347# add_test()</a> command. It adds a test to the
348# <a href="http://www.cmake.org/cmake/help/ctest-2-8-docs.html">CTest</a>-based
349# testing system. Unlike CMake's
350# <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:add_test">
351# add_test()</a>, this command can, for convenience, implicitly add
352# the necessary executable build target to the build system. Therefore,
353# instead of the name of the executable command, specify the sources of the
354# test implementation. An executable build target is then added by this
355# function using basis_add_executable(), and the built executable is used
356# as test command. If the @p UNITTEST option is given, the necessary unit
357# testing libraries which are part of the BASIS installation are added as
358# link dependencies as well as the default implementation of the main()
359# function if none of the specified source files has the suffix
360# <tt>-main</tt> or <tt>_main</tt> in the file name.
361#
362# Generator expressions as supported by CMake's
363# <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:add_test">
364# add_test()</a> command are also supported by basis_add_test() as arguments of
365# the test command. For the argument specifying the test command itself, however,
366# only the generator expression $<TARGET_FILE:tgt> is allowed. Alternatively,
367# for this special argument, the name of the executable target can be supplied
368# directly without the use of the $<TARGET_FILE:tgt> generator expression.
369# See documentation of basis_process_generator_expressions() for details on
370# the supported generator expressions.
371#
372# Example:
373# @code
374# basis_add_test (COMMAND $<TARGET_FILE:basis.testdriver> $<TARGET_FILE:myexe> ...)
375# basis_add_test (COMMAND basis.testdriver $<TARGET_FILE:myexe> ...)
376# @endcode
377#
378# @param [in] TEST_NAME Name of the test. If a source file is given
379#                       as first argument, the test name is derived
380#                       from the name of this source file and the source
381#                       file is added to the list of sources which implement
382#                       the test command.
383# @param [in] ARGN      The following parameters are parsed:
384# @par
385# <table border="0">
386#   <tr>
387#     @tp @b COMMAND cmd [arg1 [arg2 ...]] @endtp
388#     <td>The command to execute and optionally its arguments. The command
389#         can be the name of an executable target (including imported targets),
390#         or the name or path of an executable. Alternatively,
391#         a test can be build from sources and the build executable
392#         used as command. In this case, specify the sources using the
393#         @p SOURCES argument. The command name @c cmd if given is used as
394#         output name of the built executable. If you do not want to
395#         specify the name of the output executable explicitly, but have
396#         it derived from the @p TEST_NAME, do not specify the @p COMMAND
397#         option and use the @p ARGS option instead to only specify the
398#         arguments of the test command.</td>
399#   </tr>
400#   <tr>
401#     @tp @b ARGS arg1 [arg2 ...] @endtp
402#     <td>Arguments of the test command. If this option is given, the
403#         specified arguments are appended to the arguments specified
404#         already as part of the @p COMMAND option, if any.</td>
405#   </tr>
406#   <tr>
407#     @tp @b WORKING_DIRECTORY dir @endtp
408#     <td>The working directory of the test command. The generator expression
409#         $<TARGET_FILE_DIR:tgt> can be used to specify a working directory
410#         which corresponds to the output directory of a given target file.
411#         Default: @c TESTING_OUTPUT_DIR / @p TEST_NAME. </td>
412#   </tr>
413#   <tr>
414#     @tp @b CONFIGURATIONS @endtp
415#     <td>If a CONFIGURATIONS option is given then the test will be executed
416#         only when testing under one of the named configurations.</td>
417#   </tr>
418#   <tr>
419#     @tp @b SOURCES file1 [file2 ...] @endtp
420#     <td>The source files of the test. Use the @p UNITTEST option to specify
421#         that the sources are an implementation of a unit test. In this case,
422#         the default implementation of the main() function is added to the
423#         build of the test executable. However, if this list contains a
424#         file with the suffix <tt>-main</tt> or <tt>_main</tt> in the name,
425#         the default implementation of the main() function is not used.
426#         See the documentation of the @p UNITTEST option for further details.</td>
427#   </tr>
428#   <tr>
429#     @tp @b LINK_DEPENDS file1|target1 [file2|target2 ...] @endtp
430#     <td>Link dependencies of test executable build from sources.</td>
431#   </tr>
432#   <tr>
433#     @tp @b NO_DEFAULT_MAIN @endtp
434#     <td>Force that the implementation of the default main() function
435#         is not added to unit tests even if neither of the given source
436#         files has the suffix <tt>-main</tt> or <tt>_main</tt> in the
437#         file name.</td>
438#   </tr>
439#   <tr>
440#     @tp @b UNITTEST @endtp
441#     <td>Specifies that the test is a unit test. In this case, the test
442#         implementation is linked to the default unit testing framework
443#         for the used programming language which is part of the BASIS
444#         installation.</td>
445#   </tr>
446#   <tr>
447#     @tp @b WITH_EXT @endtp
448#     <td>Do not strip extension if test name is derived from source file name.</td>
449#   </tr>
450#   <tr>
451#     @tp @b ARGN @endtp
452#     <td>All other arguments are passed on to basis_add_executable() if
453#         an executable target for the test is added.</td>
454#   </tr>
455# </table>
456#
457# @returns Adds build target for test executable if test source files
458#          are given and/or adds a CTest test which executes the given
459#          test command.
460#
461# @sa basis_process_generator_expressions()
462# @sa http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:add_test
463#
464# @todo Make use of ExternalData module to fetch remote test data.
465#
466# @ingroup CMakeAPI
467function (basis_add_test TEST_NAME)
468  basis_sanitize_for_regex (R "${PROJECT_TESTING_DIR}")
469  if (NOT CMAKE_CURRENT_SOURCE_DIR MATCHES "^${R}")
470    message (FATAL_ERROR "Tests can only be added inside ${PROJECT_TESTING_DIR}!")
471  endif ()
472
473  # --------------------------------------------------------------------------
474  # parse arguments
475  CMAKE_PARSE_ARGUMENTS (
476    ARGN
477    "UNITTEST;NO_DEFAULT_MAIN;WITH_EXT;CLEAN_WORKING_DIRECTORY_BEFORE_TEST;CLEAN_WORKING_DIRECTORY_AFTER_TEST"
478    "WORKING_DIRECTORY"
479    "CONFIGURATIONS;SOURCES;LINK_DEPENDS;COMMAND;ARGS"
480    ${ARGN}
481  )
482
483  if (BASIS_DEBUG AND BASIS_VERBOSE)
484    message ("** basis_add_test():")
485    message ("**   Test:      ${TEST_NAME}")
486    message ("**   Command:   ${ARGN_COMMAND}")
487    message ("**   Arguments: ${ARGN_ARGS}")
488  endif ()
489
490  list (LENGTH ARGN_COMMAND N)
491  if (N GREATER 1)
492    list (GET ARGN_COMMAND 0 CMD)
493    list (REMOVE_AT ARGN_COMMAND 0)
494    list (APPEND ARGN_ARGS ${ARGN_COMMAND})
495    set (ARGN_COMMAND "${CMD}")
496  endif ()
497
498  # --------------------------------------------------------------------------
499  # test name
500  if (NOT ARGN_COMMAND AND NOT ARGN_SOURCES)
501    get_filename_component (ARGN_SOURCES "${TEST_NAME}" ABSOLUTE)
502    if (ARGN_WITH_EXT)
503      basis_get_source_target_name (TEST_NAME "${TEST_NAME}" NAME)
504      list (APPEND ARGN_UNPARSED_ARGUMENTS WITH_EXT) # pass on to basis_add_executable()
505    else ()
506      basis_get_source_target_name (TEST_NAME "${TEST_NAME}" NAME_WE)
507    endif ()
508  endif ()
509
510  basis_check_test_name ("${TEST_NAME}")
511  basis_make_test_uid (TEST_UID "${TEST_NAME}")
512
513  if (BASIS_DEBUG)
514    message ("** basis_add_test():")
515    message ("**   Name:      ${TEST_NAME}")
516    message ("**   Command:   ${ARGN_COMMAND}")
517    message ("**   Arguments: ${ARGN_ARGS}")
518    message ("**   Sources:   ${ARGN_SOURCES}")
519  endif ()
520
521  # --------------------------------------------------------------------------
522  # build test executable
523  set (LANGUAGE)
524  if (ARGN_SOURCES)
525    if (ARGN_UNITTEST)
526      basis_get_source_language (LANGUAGE ${ARGN_SOURCES})
527      if (NOT ARGN_NO_DEFAULT_MAIN)
528        foreach (SOURCE ${ARGN_SOURCES})
529          if (SOURCE MATCHES "-main\\.|_main\\.")
530            set (ARGN_NO_DEFAULT_MAIN 1)
531            break ()
532          endif ()
533        endforeach ()
534      endif ()
535      if (LANGUAGE MATCHES "CXX")
536        if (NOT ARGN_NO_DEFAULT_MAIN)
537          if (BASIS_TEST_MAIN_LIBRARY)
538            list (APPEND ARGN_LINK_DEPENDS "${BASIS_TEST_MAIN_LIBRARY}")
539          else ()
540            message (FATAL_ERROR "Test ${TEST_NAME} added as UNITTEST without NO_DEFAULT_MAIN option,"
541                                 " but BASIS_TEST_MAIN_LIBRARY not set. This library is part of the"
542                                 " CMake BASIS installation. When you are using the CMake BASIS Modules"
543                                 " without CMake BASIS installation, add GTest to TEST_DEPENDS and"
544                                 " set BASIS_TEST_MAIN_LIBRARY to the value of GTEST_MAIN_LIBRARY"
545                                 " before using the basis_add_test command.")
546          endif ()
547        endif ()
548        if (BASIS_TEST_LIBRARY)
549          list (APPEND ARGN_LINK_DEPENDS "${BASIS_TEST_LIBRARY}")
550        else ()
551         message (FATAL_ERROR "Test ${TEST_NAME} added as UNITTEST, but BASIS_TEST_LIBRARY not set."
552                              " This library is part of the CMake BASIS installation. When you are"
553                              " using the CMake BASIS Modules without CMake BASIS installation,"
554                              " add GTest to TEST_DEPENDS and set BASIS_TEST_LIBRARY to the value"
555                              " of GTEST_LIBRARY before using the basis_add_test command.")
556        endif ()
557      endif ()
558    endif ()
559
560    basis_add_executable (${TEST_NAME} ${ARGN_SOURCES} ${ARGN_UNPARSED_ARGUMENTS})
561    if (ARGN_LINK_DEPENDS)
562      basis_target_link_libraries (${TEST_NAME} ${ARGN_LINK_DEPENDS})
563    endif ()
564
565    if (ARGN_COMMAND)
566      basis_set_target_properties (${TEST_NAME} PROPERTIES OUTPUT_NAME ${CMD})
567    endif ()
568    set (ARGN_COMMAND ${TEST_NAME})
569
570    if (BASIS_DEBUG)
571      message ("** Added test executable ${TEST_UID} (${TEST_NAME})")
572      message ("**   Sources:           ${ARGN_SOURCES}")
573      message ("**   Link dependencies: ${ARGN_LINK_DEPENDS}")
574      message ("**   Options:           ${ARGN_UNPARSED_ARGUMENTS}")
575    endif ()
576  endif ()
577
578  # --------------------------------------------------------------------------
579  # add test
580  message (STATUS "Adding test ${TEST_UID}...")
581
582  if (NOT ARGN_WORKING_DIRECTORY)
583    set (ARGN_WORKING_DIRECTORY "${TESTING_OUTPUT_DIR}/${TEST_NAME}")
584  endif ()
585  if (ARGN_WORKING_DIRECTORY MATCHES "^\\$<(.*):(.*)>$")
586    if (NOT "${CMAKE_MATCH_1}" STREQUAL "TARGET_FILE_DIR")
587      message (FATAL_ERROR "Invalid generator expression used for working directory."
588                           " Only $<TARGET_FILE_DIR:tgt> can be used as argument"
589                           " of the WORKING_DIRECTORY option.")
590    endif ()
591    basis_get_target_location (ARGN_WORKING_DIRECTORY "${CMAKE_MATCH_2}" PATH)
592  else ()
593    if (NOT IS_DIRECTORY "${ARGN_WORKING_DIRECTORY}")
594      file (MAKE_DIRECTORY "${ARGN_WORKING_DIRECTORY}")
595    endif ()
596  endif ()
597  set (OPTS "WORKING_DIRECTORY" "${ARGN_WORKING_DIRECTORY}")
598  if (ARGN_CONFIGURATIONS)
599    list (APPEND OPTS "CONFIGURATIONS")
600    foreach (CONFIG ${ARGN_CONFIGURATIONS})
601      list (APPEND OPTS "${CONFIG}")
602    endforeach ()
603  endif ()
604
605  if (ARGN_COMMAND MATCHES "^\\$<(.*):(.*)>$")
606    if (NOT "${CMAKE_MATCH_1}" STREQUAL "TARGET_FILE")
607      message (FATAL_ERROR "Invalid generator expression used for test command."
608                           " Only $<TARGET_FILE:tgt> can be used as first"
609                           " argument of the COMMAND option.")
610    endif ()
611    basis_get_target_location (ARGN_COMMAND "${CMAKE_MATCH_2}" ABSOLUTE)
612  else ()
613    basis_get_target_uid (COMMAND_UID "${ARGN_COMMAND}")
614    if (TARGET "${COMMAND_UID}")
615      basis_get_target_location (ARGN_COMMAND "${COMMAND_UID}" ABSOLUTE)
616    endif ()
617  endif ()
618
619  basis_process_generator_expressions (ARGN_ARGS ${ARGN_ARGS})
620
621  if (BASIS_DEBUG)
622    message ("** Add test ${TEST_UID}")
623    message ("**   Command:    ${ARGN_COMMAND}")
624    message ("**   Arguments:  ${ARGN_ARGS}")
625    message ("**   Working in: ${ARGN_WORKING_DIRECTORY}")
626  endif ()
627
628  add_test (NAME ${TEST_UID} COMMAND ${ARGN_COMMAND} ${ARGN_ARGS} ${OPTS})
629
630  # especially in case of C++ unit tests, if the linkage of the tests is done
631  # incorrectly, no tests are actually run and the unit test passes
632  # therefore, add this fail regular expression to identify such issues
633  set_tests_properties (${TEST_UID} PROPERTIES FAIL_REGULAR_EXPRESSION "(\\[ *PASSED *\\]|Ran) 0 tests|No tests were found!!!")
634
635  message (STATUS "Adding test ${TEST_UID}... - done")
636endfunction ()
637
638# ----------------------------------------------------------------------------
639## @brief Add tests of default options for given executable.
640#
641# @par Default options:
642# <table border="0">
643#   <tr>
644#     @tp <b>--helpshort</b> @endtp
645#     <td>Short help. The output has to match the regular expression
646#         "[Uu]sage:\n\s*\<executable name\>", where \<executable name\>
647#         is the name of the tested executable.</td>
648#   </tr>
649#   <tr>
650#     @tp <b>--help, -h</b> @endtp
651#     <td>Help screen. Simply tests if the option is accepted.</td>
652#   </tr>
653#   <tr>
654#     @tp <b>--version</b> @endtp
655#     <td>Version information. Output has to include the project version string.</td>
656#   </tr>
657#   <tr>
658#     @tp <b>--verbose, -v</b> @endtp
659#     <td>Increase verbosity of output messages. Simply tests if the option is accepted.</td>
660#   </tr>
661# </table>
662#
663# @param [in] TARGET_NAME Name of executable or script target.
664#
665# @returns Adds tests for the default options of the specified executable.
666function (basis_add_tests_of_default_options TARGET_NAME)
667  basis_get_target_uid (TARGET_UID "${TARGET_NAME}")
668
669  message (STATUS "Adding tests of default options for ${TARGET_UID}...")
670
671  if (NOT TARGET "${TARGET_UID}")
672    message (FATAL_ERROR "Unknown target ${TARGET_UID}.")
673  endif ()
674
675  # get executable name
676  get_target_property (PREFIX      ${TARGET_UID} "PREFIX")
677  get_target_property (OUTPUT_NAME ${TARGET_UID} "OUTPUT_NAME")
678  get_target_property (SUFFIX      ${TARGET_UID} "SUFFIX")
679
680  if (NOT OUTPUT_NAME)
681    set (EXEC_NAME "${TARGET_UID}")
682  endif ()
683  if (PREFIX)
684    set (EXEC_NAME "${PREFIX}${EXEC_NAME}")
685  endif ()
686  if (SUFFIX)
687    set (EXEC_NAME "${EXEC_NAME}${SUFFIX}")
688  endif ()
689
690  # get absolute path to executable
691  get_target_property (EXEC_DIR ${TARGET_UID} "RUNTIME_OUTPUT_DIRECTORY")
692
693  # executable command
694  set (EXEC_CMD "${EXEC_DIR}/${EXEC_NAME}")
695
696  # test option: -v
697  basis_add_test (${EXEC}VersionS "${EXEC_CMD}" "-v")
698
699  set_tests_properties (
700    ${EXEC}VersionS
701    PROPERTIES
702      PASS_REGULAR_EXPRESSION "${EXEC} ${PROJECT_VERSION}"
703  )
704
705  # test option: --version
706  basis_add_test (${EXEC}VersionL "${EXEC_CMD}" "--version")
707
708  set_tests_properties (
709    ${EXEC}VersionL
710    PROPERTIES
711      PASS_REGULAR_EXPRESSION "${EXEC} ${PROJECT_VERSION}"
712  )
713
714  # test option: -h
715  basis_add_test (${EXEC}HelpS "${EXEC_CMD}" "-h")
716
717  # test option: --help
718  basis_add_test (${EXEC}HelpL "${EXEC_CMD}" "--help")
719
720  # test option: --helpshort
721  basis_add_test (${EXEC}UsageL "${EXEC_CMD}" "--helpshort")
722
723  set_tests_properties (
724    ${EXEC}UsageL
725    PROPERTIES
726      PASS_REGULAR_EXPRESSION "[Uu]sage:(\n)( )*${EXEC_NAME}"
727  )
728
729  message (STATUS "Adding tests of default options for ${EXEC}... - done")
730endfunction ()
731
732
733## @}
734# end of Doxygen group
735