2017-04-11 16:51:02 +02:00
#==================================================================================================#
# supported macros #
# - TEST_CASE, #
2020-09-23 18:13:29 +02:00
# - TEMPLATE_TEST_CASE #
2017-04-11 16:51:02 +02:00
# - SCENARIO, #
# - TEST_CASE_METHOD, #
# - CATCH_TEST_CASE, #
2020-09-23 18:13:29 +02:00
# - CATCH_TEMPLATE_TEST_CASE #
2017-04-11 16:51:02 +02:00
# - CATCH_SCENARIO, #
# - CATCH_TEST_CASE_METHOD. #
# #
# Usage #
# 1. make sure this module is in the path or add this otherwise: #
# set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake.modules/") #
# 2. make sure that you've enabled testing option for the project by the call: #
# enable_testing() #
# 3. add the lines to the script for testing target (sample CMakeLists.txt): #
# project(testing_target) #
# set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake.modules/") #
# enable_testing() #
# #
# find_path(CATCH_INCLUDE_DIR "catch.hpp") #
# include_directories(${INCLUDE_DIRECTORIES} ${CATCH_INCLUDE_DIR}) #
# #
# file(GLOB SOURCE_FILES "*.cpp") #
# add_executable(${PROJECT_NAME} ${SOURCE_FILES}) #
# #
# include(ParseAndAddCatchTests) #
# ParseAndAddCatchTests(${PROJECT_NAME}) #
2017-05-27 14:42:54 +02:00
# #
2017-05-18 14:53:35 +02:00
# The following variables affect the behavior of the script: #
# #
# PARSE_CATCH_TESTS_VERBOSE (Default OFF) #
2017-05-27 14:42:54 +02:00
# -- enables debug messages #
2017-08-01 17:33:53 +02:00
# PARSE_CATCH_TESTS_NO_HIDDEN_TESTS (Default OFF) #
# -- excludes tests marked with [!hide], [.] or [.foo] tags #
# PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME (Default ON) #
# -- adds fixture class name to the test name #
# PARSE_CATCH_TESTS_ADD_TARGET_IN_TEST_NAME (Default ON) #
# -- adds cmake target name to the test name #
2017-10-11 14:01:06 +02:00
# PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS (Default OFF) #
# -- causes CMake to rerun when file with tests changes so that new tests will be discovered #
2017-05-18 14:53:35 +02:00
# #
2018-10-18 01:19:36 +02:00
# One can also set (locally) the optional variable OptionalCatchTestLauncher to precise the way #
# a test should be run. For instance to use test MPI, one can write #
# set(OptionalCatchTestLauncher ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${NUMPROC}) #
# just before calling this ParseAndAddCatchTests function #
# #
2019-03-07 15:07:59 +01:00
# The AdditionalCatchParameters optional variable can be used to pass extra argument to the test #
# command. For example, to include successful tests in the output, one can write #
# set(AdditionalCatchParameters --success) #
# #
2019-03-07 15:07:53 +01:00
# After the script, the ParseAndAddCatchTests_TESTS property for the target, and for each source #
# file in the target is set, and contains the list of the tests extracted from that target, or #
# from that file. This is useful, for example to add further labels or properties to the tests. #
# #
2017-04-11 16:51:02 +02:00
#==================================================================================================#
2019-03-07 16:01:19 +01:00
if ( CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 2.8.8 )
message ( FATAL_ERROR "ParseAndAddCatchTests requires CMake 2.8.8 or newer" )
endif ( )
2017-04-11 16:51:02 +02:00
2017-05-18 14:53:35 +02:00
option ( PARSE_CATCH_TESTS_VERBOSE "Print Catch to CTest parser debug messages" OFF )
2017-08-01 17:33:53 +02:00
option ( PARSE_CATCH_TESTS_NO_HIDDEN_TESTS "Exclude tests with [!hide], [.] or [.foo] tags" OFF )
option ( PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME "Add fixture class name to the test name" ON )
option ( PARSE_CATCH_TESTS_ADD_TARGET_IN_TEST_NAME "Add target name to the test name" ON )
2017-10-11 14:01:06 +02:00
option ( PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS "Add test file to CMAKE_CONFIGURE_DEPENDS property" OFF )
2017-05-18 14:53:35 +02:00
2019-03-07 15:59:53 +01:00
function ( ParseAndAddCatchTests_PrintDebugMessage )
2017-05-18 14:53:35 +02:00
if ( PARSE_CATCH_TESTS_VERBOSE )
message ( STATUS "ParseAndAddCatchTests: ${ARGV}" )
endif ( )
endfunction ( )
2017-04-11 16:51:02 +02:00
# This removes the contents between
2017-05-27 14:42:54 +02:00
# - block comments (i.e. /* ... */)
# - full line comments (i.e. // ... )
2017-04-11 16:51:02 +02:00
# contents have been read into '${CppCode}'.
# !keep partial line comments
2019-03-07 15:59:53 +01:00
function ( ParseAndAddCatchTests_RemoveComments CppCode )
2017-04-11 16:51:02 +02:00
string ( ASCII 2 CMakeBeginBlockComment )
string ( ASCII 3 CMakeEndBlockComment )
string ( REGEX REPLACE "/\\*" "${CMakeBeginBlockComment}" ${ CppCode } "${${CppCode}}" )
string ( REGEX REPLACE "\\*/" "${CMakeEndBlockComment}" ${ CppCode } "${${CppCode}}" )
string ( REGEX REPLACE "${CMakeBeginBlockComment}[^${CMakeEndBlockComment}]*${CMakeEndBlockComment}" "" ${ CppCode } "${${CppCode}}" )
string ( REGEX REPLACE "\n[ \t]*//+[^\n]+" "\n" ${ CppCode } "${${CppCode}}" )
set ( ${ CppCode } "${${CppCode}}" PARENT_SCOPE )
endfunction ( )
# Worker function
2019-03-07 15:59:53 +01:00
function ( ParseAndAddCatchTests_ParseFile SourceFile TestTarget )
2019-04-14 17:39:26 +02:00
# If SourceFile is an object library, do not scan it (as it is not a file). Exit without giving a warning about a missing file.
if ( SourceFile MATCHES "\\\$<TARGET_OBJECTS:.+>" )
ParseAndAddCatchTests_PrintDebugMessage ( "Detected OBJECT library: ${SourceFile} this will not be scanned for tests." )
return ( )
endif ( )
2017-05-27 14:42:54 +02:00
# According to CMake docs EXISTS behavior is well-defined only for full paths.
2017-05-18 14:51:44 +02:00
get_filename_component ( SourceFile ${ SourceFile } ABSOLUTE )
2017-04-11 16:51:02 +02:00
if ( NOT EXISTS ${ SourceFile } )
2017-05-01 18:17:57 +02:00
message ( WARNING "Cannot find source file: ${SourceFile}" )
2017-04-11 16:51:02 +02:00
return ( )
endif ( )
2019-03-07 15:59:53 +01:00
ParseAndAddCatchTests_PrintDebugMessage ( "parsing ${SourceFile}" )
2017-04-11 16:51:02 +02:00
file ( STRINGS ${ SourceFile } Contents NEWLINE_CONSUME )
# Remove block and fullline comments
2019-03-07 15:59:53 +01:00
ParseAndAddCatchTests_RemoveComments ( Contents )
2017-04-11 16:51:02 +02:00
# Find definition of test names
2020-10-19 17:57:00 +02:00
# https://regex101.com/r/JygOND/1
string ( REGEX MATCHALL "[ \t]*(CATCH_)?(TEMPLATE_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)[ \t]*\\([ \t\n]*\" [^\ "]*\" [ \t\n]*(,[ \t\n]*\"[^\"]*\")?(,[ \t\n]*[^\,\)]*)*\\)[ \t\n]*\{+[ \t]*(//[^\n]*[Tt][Ii][Mm][Ee][Oo][Uu][Tt][ \t]*[0-9]+)*" Tests "${Contents}" )
2017-04-11 16:51:02 +02:00
2017-10-11 14:01:06 +02:00
if ( PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS AND Tests )
2019-03-07 15:59:53 +01:00
ParseAndAddCatchTests_PrintDebugMessage ( "Adding ${SourceFile} to CMAKE_CONFIGURE_DEPENDS property" )
2017-10-11 14:01:06 +02:00
set_property (
D I R E C T O R Y
A P P E N D
P R O P E R T Y C M A K E _ C O N F I G U R E _ D E P E N D S $ { S o u r c e F i l e }
)
endif ( )
2020-10-20 09:11:22 +02:00
# check CMP0110 policy for new add_test() behavior
if ( POLICY CMP0110 )
cmake_policy ( GET CMP0110 _cmp0110_value ) # new add_test() behavior
else ( )
# just to be thorough explicitly set the variable
set ( _cmp0110_value )
endif ( )
2017-04-11 16:51:02 +02:00
foreach ( TestName ${ Tests } )
# Strip newlines
string ( REGEX REPLACE "\\\\\n|\n" "" TestName "${TestName}" )
# Get test type and fixture if applicable
2020-09-23 18:13:29 +02:00
string ( REGEX MATCH "(CATCH_)?(TEMPLATE_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)[ \t]*\\([^,^\" ]* " TestTypeAndFixture " ${ TestName } " )
string ( REGEX MATCH "(CATCH_)?(TEMPLATE_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)" TestType "${TestTypeAndFixture}" )
2019-01-10 14:47:19 +01:00
string ( REGEX REPLACE "${TestType}\\([ \t]*" "" TestFixture "${TestTypeAndFixture}" )
2017-04-11 16:51:02 +02:00
# Get string parts of test definition
string ( REGEX MATCHALL "\" +([^\\^\ "]|\\\\\" ) + \ " + " T e s t S t r i n g s " $ { T e s t N a m e } " )
# Strip wrapping quotation marks
string ( REGEX REPLACE "^\" ( .* ) \"$" "\\1" TestStrings "${TestStrings}" )
string ( REPLACE "\" ;\ "" ";" TestStrings "${TestStrings}" )
# Validate that a test name and tags have been provided
list ( LENGTH TestStrings TestStringsLength )
if ( TestStringsLength GREATER 2 OR TestStringsLength LESS 1 )
message ( FATAL_ERROR "You must provide a valid test name and tags for all tests in ${SourceFile}" )
endif ( )
# Assign name and tags
list ( GET TestStrings 0 Name )
if ( "${TestType}" STREQUAL "SCENARIO" )
set ( Name "Scenario: ${Name}" )
endif ( )
2020-03-23 03:24:10 +01:00
if ( PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME AND "${TestType}" MATCHES "(CATCH_)?TEST_CASE_METHOD" AND TestFixture )
2017-04-11 16:51:02 +02:00
set ( CTestName "${TestFixture}:${Name}" )
else ( )
set ( CTestName "${Name}" )
endif ( )
2017-08-01 17:33:53 +02:00
if ( PARSE_CATCH_TESTS_ADD_TARGET_IN_TEST_NAME )
set ( CTestName "${TestTarget}:${CTestName}" )
endif ( )
2017-05-18 15:00:18 +02:00
# add target to labels to enable running all tests added from this target
set ( Labels ${ TestTarget } )
2017-04-11 16:51:02 +02:00
if ( TestStringsLength EQUAL 2 )
list ( GET TestStrings 1 Tags )
string ( TOLOWER "${Tags}" Tags )
2017-05-18 15:00:18 +02:00
# remove target from labels if the test is hidden
if ( "${Tags}" MATCHES ".*\\[!?(hide|\\.)\\].*" )
list ( REMOVE_ITEM Labels ${ TestTarget } )
endif ( )
2017-04-11 16:51:02 +02:00
string ( REPLACE "]" ";" Tags "${Tags}" )
string ( REPLACE "[" "" Tags "${Tags}" )
2018-07-10 02:49:34 +02:00
else ( )
# unset tags variable from previous loop
unset ( Tags )
2017-04-11 16:51:02 +02:00
endif ( )
2017-05-27 14:42:54 +02:00
2017-05-18 15:00:18 +02:00
list ( APPEND Labels ${ Tags } )
2017-05-27 14:42:54 +02:00
2017-08-01 17:33:53 +02:00
set ( HiddenTagFound OFF )
foreach ( label ${ Labels } )
string ( REGEX MATCH "^!hide|^\\." result ${ label } )
if ( result )
set ( HiddenTagFound ON )
break ( )
endif ( result )
endforeach ( label )
2019-01-23 15:03:33 +01:00
if ( PARSE_CATCH_TESTS_NO_HIDDEN_TESTS AND ${ HiddenTagFound } AND ${ CMAKE_VERSION } VERSION_LESS "3.9" )
2019-03-07 15:59:53 +01:00
ParseAndAddCatchTests_PrintDebugMessage ( "Skipping test \" ${ CTestName } \" as it has [!hide], [.] or [.foo] label " )
2017-08-01 17:33:53 +02:00
else ( )
2019-03-07 15:59:53 +01:00
ParseAndAddCatchTests_PrintDebugMessage ( "Adding test \" ${ CTestName } \"")
2017-08-01 17:33:53 +02:00
if ( Labels )
2019-03-07 15:59:53 +01:00
ParseAndAddCatchTests_PrintDebugMessage ( "Setting labels to ${Labels}" )
2017-08-01 17:33:53 +02:00
endif ( )
2017-04-11 16:51:02 +02:00
2019-03-03 13:10:08 +01:00
# Escape commas in the test spec
string ( REPLACE "," "\\," Name ${ Name } )
2021-04-07 20:43:50 +02:00
# Work around CMake 3.18.0 change in `add_test()`, before the escaped quotes were necessary,
2020-07-31 08:24:44 +02:00
# only with CMake 3.18.0 the escaped double quotes confuse the call. This change is reverted in 3.18.1
2020-10-20 09:11:22 +02:00
# And properly introduced in 3.19 with the CMP0110 policy
if ( _cmp0110_value STREQUAL "NEW" OR ${ CMAKE_VERSION } VERSION_EQUAL "3.18" )
ParseAndAddCatchTests_PrintDebugMessage ( "CMP0110 set to NEW, no need for add_test(\" \") workaround " )
else ( )
ParseAndAddCatchTests_PrintDebugMessage ( "CMP0110 set to OLD adding \" \" for add_test( ) w o r k a r o u n d " )
2020-07-16 11:45:38 +02:00
set ( CTestName "\" ${ CTestName } \"")
endif ( )
2020-09-23 18:13:29 +02:00
2020-10-20 11:24:00 +02:00
# Handle template test cases
if ( "${TestTypeAndFixture}" MATCHES ".*TEMPLATE_.*" )
set ( Name "${Name} - *" )
endif ( )
2020-09-23 18:13:29 +02:00
2017-08-01 17:33:53 +02:00
# Add the test and set its properties
2020-07-16 11:45:38 +02:00
add_test ( NAME "${CTestName}" COMMAND ${ OptionalCatchTestLauncher } $< TARGET_FILE:${TestTarget} > ${ Name } ${ AdditionalCatchParameters } )
2019-01-23 15:03:33 +01:00
# Old CMake versions do not document VERSION_GREATER_EQUAL, so we use VERSION_GREATER with 3.8 instead
if ( PARSE_CATCH_TESTS_NO_HIDDEN_TESTS AND ${ HiddenTagFound } AND ${ CMAKE_VERSION } VERSION_GREATER "3.8" )
2019-03-07 15:59:53 +01:00
ParseAndAddCatchTests_PrintDebugMessage ( "Setting DISABLED test property" )
2020-07-16 11:45:38 +02:00
set_tests_properties ( "${CTestName}" PROPERTIES DISABLED ON )
2018-11-21 09:04:00 +01:00
else ( )
2020-07-16 11:45:38 +02:00
set_tests_properties ( "${CTestName}" PROPERTIES FAIL_REGULAR_EXPRESSION "No tests ran"
2018-11-21 09:04:00 +01:00
L A B E L S " $ { L a b e l s } " )
endif ( )
2019-03-07 15:07:53 +01:00
set_property (
T A R G E T $ { T e s t T a r g e t }
A P P E N D
2020-07-16 11:45:38 +02:00
P R O P E R T Y P a r s e A n d A d d C a t c h T e s t s _ T E S T S " $ { C T e s t N a m e } " )
2019-03-07 15:07:53 +01:00
set_property (
S O U R C E $ { S o u r c e F i l e }
A P P E N D
2020-07-16 11:45:38 +02:00
P R O P E R T Y P a r s e A n d A d d C a t c h T e s t s _ T E S T S " $ { C T e s t N a m e } " )
2017-08-01 17:33:53 +02:00
endif ( )
2017-04-11 16:51:02 +02:00
2019-01-23 15:03:33 +01:00
2017-04-11 16:51:02 +02:00
endforeach ( )
endfunction ( )
# entry point
function ( ParseAndAddCatchTests TestTarget )
2020-12-17 14:02:48 +01:00
message ( DEPRECATION "ParseAndAddCatchTest: function deprecated because of possibility of missed test cases. Consider using 'catch_discover_tests' from 'Catch.cmake'" )
2019-03-07 15:59:53 +01:00
ParseAndAddCatchTests_PrintDebugMessage ( "Started parsing ${TestTarget}" )
2017-04-11 16:51:02 +02:00
get_target_property ( SourceFiles ${ TestTarget } SOURCES )
2019-03-07 15:59:53 +01:00
ParseAndAddCatchTests_PrintDebugMessage ( "Found the following sources: ${SourceFiles}" )
2017-04-11 16:51:02 +02:00
foreach ( SourceFile ${ SourceFiles } )
2019-03-07 15:59:53 +01:00
ParseAndAddCatchTests_ParseFile ( ${ SourceFile } ${ TestTarget } )
2017-04-11 16:51:02 +02:00
endforeach ( )
2019-03-07 15:59:53 +01:00
ParseAndAddCatchTests_PrintDebugMessage ( "Finished parsing ${TestTarget}" )
2017-05-01 18:17:57 +02:00
endfunction ( )