|
| 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 | +# # |
| 34 | +#==================================================================================================# |
| 35 | + |
| 36 | +cmake_minimum_required(VERSION 2.8.8) |
| 37 | + |
| 38 | +option(PARSE_CATCH_TESTS_VERBOSE "Print Catch to CTest parser debug messages" OFF) |
| 39 | + |
| 40 | +function(PrintDebugMessage) |
| 41 | + if(PARSE_CATCH_TESTS_VERBOSE) |
| 42 | + message(STATUS "ParseAndAddCatchTests: ${ARGV}") |
| 43 | + endif() |
| 44 | +endfunction() |
| 45 | + |
| 46 | +# This removes the contents between |
| 47 | +# - block comments (i.e. /* ... */) |
| 48 | +# - full line comments (i.e. // ... ) |
| 49 | +# contents have been read into '${CppCode}'. |
| 50 | +# !keep partial line comments |
| 51 | +function(RemoveComments CppCode) |
| 52 | + string(ASCII 2 CMakeBeginBlockComment) |
| 53 | + string(ASCII 3 CMakeEndBlockComment) |
| 54 | + string(REGEX REPLACE "/\\*" "${CMakeBeginBlockComment}" ${CppCode} "${${CppCode}}") |
| 55 | + string(REGEX REPLACE "\\*/" "${CMakeEndBlockComment}" ${CppCode} "${${CppCode}}") |
| 56 | + string(REGEX REPLACE "${CMakeBeginBlockComment}[^${CMakeEndBlockComment}]*${CMakeEndBlockComment}" "" ${CppCode} "${${CppCode}}") |
| 57 | + string(REGEX REPLACE "\n[ \t]*//+[^\n]+" "\n" ${CppCode} "${${CppCode}}") |
| 58 | + |
| 59 | + set(${CppCode} "${${CppCode}}" PARENT_SCOPE) |
| 60 | +endfunction() |
| 61 | + |
| 62 | +# Worker function |
| 63 | +function(ParseFile SourceFile TestTarget) |
| 64 | + # According to CMake docs EXISTS behavior is well-defined only for full paths. |
| 65 | + get_filename_component(SourceFile ${SourceFile} ABSOLUTE) |
| 66 | + if(NOT EXISTS ${SourceFile}) |
| 67 | + message(WARNING "Cannot find source file: ${SourceFile}") |
| 68 | + return() |
| 69 | + endif() |
| 70 | + PrintDebugMessage("parsing ${SourceFile}") |
| 71 | + file(STRINGS ${SourceFile} Contents NEWLINE_CONSUME) |
| 72 | + |
| 73 | + # Remove block and fullline comments |
| 74 | + RemoveComments(Contents) |
| 75 | + |
| 76 | + # Find definition of test names |
| 77 | + 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}") |
| 78 | + |
| 79 | + foreach(TestName ${Tests}) |
| 80 | + # Strip newlines |
| 81 | + string(REGEX REPLACE "\\\\\n|\n" "" TestName "${TestName}") |
| 82 | + |
| 83 | + # Get test type and fixture if applicable |
| 84 | + string(REGEX MATCH "(CATCH_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)[ \t]*\\([^,^\"]*" TestTypeAndFixture "${TestName}") |
| 85 | + string(REGEX MATCH "(CATCH_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)" TestType "${TestTypeAndFixture}") |
| 86 | + string(REPLACE "${TestType}(" "" TestFixture "${TestTypeAndFixture}") |
| 87 | + |
| 88 | + # Get string parts of test definition |
| 89 | + string(REGEX MATCHALL "\"+([^\\^\"]|\\\\\")+\"+" TestStrings "${TestName}") |
| 90 | + |
| 91 | + # Strip wrapping quotation marks |
| 92 | + string(REGEX REPLACE "^\"(.*)\"$" "\\1" TestStrings "${TestStrings}") |
| 93 | + string(REPLACE "\";\"" ";" TestStrings "${TestStrings}") |
| 94 | + |
| 95 | + # Validate that a test name and tags have been provided |
| 96 | + list(LENGTH TestStrings TestStringsLength) |
| 97 | + if(TestStringsLength GREATER 2 OR TestStringsLength LESS 1) |
| 98 | + message(FATAL_ERROR "You must provide a valid test name and tags for all tests in ${SourceFile}") |
| 99 | + endif() |
| 100 | + |
| 101 | + # Assign name and tags |
| 102 | + list(GET TestStrings 0 Name) |
| 103 | + if("${TestType}" STREQUAL "SCENARIO") |
| 104 | + set(Name "Scenario: ${Name}") |
| 105 | + endif() |
| 106 | + if(TestFixture) |
| 107 | + set(CTestName "${TestFixture}:${Name}") |
| 108 | + else() |
| 109 | + set(CTestName "${Name}") |
| 110 | + endif() |
| 111 | + set(CTestName "${TestTarget}:${CTestName}") |
| 112 | + # add target to labels to enable running all tests added from this target |
| 113 | + set(Labels ${TestTarget}) |
| 114 | + if(TestStringsLength EQUAL 2) |
| 115 | + list(GET TestStrings 1 Tags) |
| 116 | + string(TOLOWER "${Tags}" Tags) |
| 117 | + # remove target from labels if the test is hidden |
| 118 | + if("${Tags}" MATCHES ".*\\[!?(hide|\\.)\\].*") |
| 119 | + list(REMOVE_ITEM Labels ${TestTarget}) |
| 120 | + endif() |
| 121 | + string(REPLACE "]" ";" Tags "${Tags}") |
| 122 | + string(REPLACE "[" "" Tags "${Tags}") |
| 123 | + endif() |
| 124 | + |
| 125 | + list(APPEND Labels ${Tags}) |
| 126 | + |
| 127 | + PrintDebugMessage("Adding test \"${CTestName}\"") |
| 128 | + if(Labels) |
| 129 | + PrintDebugMessage("Setting labels to ${Labels}") |
| 130 | + endif() |
| 131 | + |
| 132 | + # Add the test and set its properties |
| 133 | + add_test(NAME "\"${CTestName}\"" COMMAND ${TestTarget} ${Name} ${AdditionalCatchParameters}) |
| 134 | + set_tests_properties("\"${CTestName}\"" PROPERTIES FAIL_REGULAR_EXPRESSION "No tests ran" |
| 135 | + LABELS "${Labels}") |
| 136 | + |
| 137 | + endforeach() |
| 138 | +endfunction() |
| 139 | + |
| 140 | +# entry point |
| 141 | +function(ParseAndAddCatchTests TestTarget) |
| 142 | + PrintDebugMessage("Started parsing ${TestTarget}") |
| 143 | + get_target_property(SourceFiles ${TestTarget} SOURCES) |
| 144 | + PrintDebugMessage("Found the following sources: ${SourceFiles}") |
| 145 | + foreach(SourceFile ${SourceFiles}) |
| 146 | + ParseFile(${SourceFile} ${TestTarget}) |
| 147 | + endforeach() |
| 148 | + PrintDebugMessage("Finished parsing ${TestTarget}") |
| 149 | +endfunction() |
0 commit comments