1#==================================================================================================# 2# supported macros # 3# - TEST_CASE, # 4# - SCENARIO, # 5# - TEST_CASE_METHOD, # 6# - CATCH_TEST_CASE, # 7# - CATCH_SCENARIO, # 8# - CATCH_TEST_CASE_METHOD. # 9# # 10# Usage # 11# 1. make sure this module is in the path or add this otherwise: # 12# set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake.modules/") # 13# 2. make sure that you've enabled testing option for the project by the call: # 14# enable_testing() # 15# 3. add the lines to the script for testing target (sample CMakeLists.txt): # 16# project(testing_target) # 17# set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake.modules/") # 18# enable_testing() # 19# # 20# find_path(CATCH_INCLUDE_DIR "catch.hpp") # 21# include_directories(${INCLUDE_DIRECTORIES} ${CATCH_INCLUDE_DIR}) # 22# # 23# file(GLOB SOURCE_FILES "*.cpp") # 24# add_executable(${PROJECT_NAME} ${SOURCE_FILES}) # 25# # 26# include(ParseAndAddCatchTests) # 27# ParseAndAddCatchTests(${PROJECT_NAME}) # 28# # 29# The following variables affect the behavior of the script: # 30# # 31# PARSE_CATCH_TESTS_VERBOSE (Default OFF) # 32# -- enables debug messages # 33# PARSE_CATCH_TESTS_NO_HIDDEN_TESTS (Default OFF) # 34# -- excludes tests marked with [!hide], [.] or [.foo] tags # 35# PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME (Default ON) # 36# -- adds fixture class name to the test name # 37# PARSE_CATCH_TESTS_ADD_TARGET_IN_TEST_NAME (Default ON) # 38# -- adds cmake target name to the test name # 39# # 40#==================================================================================================# 41 42cmake_minimum_required(VERSION 2.8.8) 43 44option(PARSE_CATCH_TESTS_VERBOSE "Print Catch to CTest parser debug messages" OFF) 45option(PARSE_CATCH_TESTS_NO_HIDDEN_TESTS "Exclude tests with [!hide], [.] or [.foo] tags" OFF) 46option(PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME "Add fixture class name to the test name" ON) 47option(PARSE_CATCH_TESTS_ADD_TARGET_IN_TEST_NAME "Add target name to the test name" ON) 48 49function(PrintDebugMessage) 50 if(PARSE_CATCH_TESTS_VERBOSE) 51 message(STATUS "ParseAndAddCatchTests: ${ARGV}") 52 endif() 53endfunction() 54 55# This removes the contents between 56# - block comments (i.e. /* ... */) 57# - full line comments (i.e. // ... ) 58# contents have been read into '${CppCode}'. 59# !keep partial line comments 60function(RemoveComments CppCode) 61 string(ASCII 2 CMakeBeginBlockComment) 62 string(ASCII 3 CMakeEndBlockComment) 63 string(REGEX REPLACE "/\\*" "${CMakeBeginBlockComment}" ${CppCode} "${${CppCode}}") 64 string(REGEX REPLACE "\\*/" "${CMakeEndBlockComment}" ${CppCode} "${${CppCode}}") 65 string(REGEX REPLACE "${CMakeBeginBlockComment}[^${CMakeEndBlockComment}]*${CMakeEndBlockComment}" "" ${CppCode} "${${CppCode}}") 66 string(REGEX REPLACE "\n[ \t]*//+[^\n]+" "\n" ${CppCode} "${${CppCode}}") 67 68 set(${CppCode} "${${CppCode}}" PARENT_SCOPE) 69endfunction() 70 71# Worker function 72function(ParseFile SourceFile TestTarget) 73 # According to CMake docs EXISTS behavior is well-defined only for full paths. 74 get_filename_component(SourceFile ${SourceFile} ABSOLUTE) 75 if(NOT EXISTS ${SourceFile}) 76 message(WARNING "Cannot find source file: ${SourceFile}") 77 return() 78 endif() 79 PrintDebugMessage("parsing ${SourceFile}") 80 file(STRINGS ${SourceFile} Contents NEWLINE_CONSUME) 81 82 # Remove block and fullline comments 83 RemoveComments(Contents) 84 85 # Find definition of test names 86 string(REGEX MATCHALL "[ \t]*(CATCH_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)[ \t]*\\([^\)]+\\)+[ \t\n]*{+[ \t]*(//[^\n]*[Tt][Ii][Mm][Ee][Oo][Uu][Tt][ \t]*[0-9]+)*" Tests "${Contents}") 87 88 foreach(TestName ${Tests}) 89 # Strip newlines 90 string(REGEX REPLACE "\\\\\n|\n" "" TestName "${TestName}") 91 92 # Get test type and fixture if applicable 93 string(REGEX MATCH "(CATCH_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)[ \t]*\\([^,^\"]*" TestTypeAndFixture "${TestName}") 94 string(REGEX MATCH "(CATCH_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)" TestType "${TestTypeAndFixture}") 95 string(REPLACE "${TestType}(" "" TestFixture "${TestTypeAndFixture}") 96 97 # Get string parts of test definition 98 string(REGEX MATCHALL "\"+([^\\^\"]|\\\\\")+\"+" TestStrings "${TestName}") 99 100 # Strip wrapping quotation marks 101 string(REGEX REPLACE "^\"(.*)\"$" "\\1" TestStrings "${TestStrings}") 102 string(REPLACE "\";\"" ";" TestStrings "${TestStrings}") 103 104 # Validate that a test name and tags have been provided 105 list(LENGTH TestStrings TestStringsLength) 106 if(TestStringsLength GREATER 2 OR TestStringsLength LESS 1) 107 message(FATAL_ERROR "You must provide a valid test name and tags for all tests in ${SourceFile}") 108 endif() 109 110 # Assign name and tags 111 list(GET TestStrings 0 Name) 112 if("${TestType}" STREQUAL "SCENARIO") 113 set(Name "Scenario: ${Name}") 114 endif() 115 if(PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME AND TestFixture) 116 set(CTestName "${TestFixture}:${Name}") 117 else() 118 set(CTestName "${Name}") 119 endif() 120 if(PARSE_CATCH_TESTS_ADD_TARGET_IN_TEST_NAME) 121 set(CTestName "${TestTarget}:${CTestName}") 122 endif() 123 # add target to labels to enable running all tests added from this target 124 set(Labels ${TestTarget}) 125 if(TestStringsLength EQUAL 2) 126 list(GET TestStrings 1 Tags) 127 string(TOLOWER "${Tags}" Tags) 128 # remove target from labels if the test is hidden 129 if("${Tags}" MATCHES ".*\\[!?(hide|\\.)\\].*") 130 list(REMOVE_ITEM Labels ${TestTarget}) 131 endif() 132 string(REPLACE "]" ";" Tags "${Tags}") 133 string(REPLACE "[" "" Tags "${Tags}") 134 endif() 135 136 list(APPEND Labels ${Tags}) 137 138 list(FIND Labels "!hide" IndexOfHideLabel) 139 set(HiddenTagFound OFF) 140 foreach(label ${Labels}) 141 string(REGEX MATCH "^!hide|^\\." result ${label}) 142 if(result) 143 set(HiddenTagFound ON) 144 break() 145 endif(result) 146 endforeach(label) 147 if(PARSE_CATCH_TESTS_NO_HIDDEN_TESTS AND ${HiddenTagFound}) 148 PrintDebugMessage("Skipping test \"${CTestName}\" as it has [!hide], [.] or [.foo] label") 149 else() 150 PrintDebugMessage("Adding test \"${CTestName}\"") 151 if(Labels) 152 PrintDebugMessage("Setting labels to ${Labels}") 153 endif() 154 155 # Add the test and set its properties 156 add_test(NAME "\"${CTestName}\"" COMMAND ${TestTarget} ${Name} ${AdditionalCatchParameters}) 157 set_tests_properties("\"${CTestName}\"" PROPERTIES FAIL_REGULAR_EXPRESSION "No tests ran" 158 LABELS "${Labels}") 159 endif() 160 161 endforeach() 162endfunction() 163 164# entry point 165function(ParseAndAddCatchTests TestTarget) 166 PrintDebugMessage("Started parsing ${TestTarget}") 167 get_target_property(SourceFiles ${TestTarget} SOURCES) 168 PrintDebugMessage("Found the following sources: ${SourceFiles}") 169 foreach(SourceFile ${SourceFiles}) 170 ParseFile(${SourceFile} ${TestTarget}) 171 endforeach() 172 PrintDebugMessage("Finished parsing ${TestTarget}") 173endfunction() 174