GoogleTestAddTests.cmake 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. # Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. # file Copyright.txt or https://cmake.org/licensing for details.
  3. cmake_minimum_required(VERSION 3.30)
  4. cmake_policy(SET CMP0174 NEW) # TODO: Remove this when we can update the above to 3.31
  5. function(add_command name test_name)
  6. set(args "")
  7. foreach(arg ${ARGN})
  8. if(arg MATCHES "[^-./:a-zA-Z0-9_]")
  9. string(APPEND args " [==[${arg}]==]")
  10. else()
  11. string(APPEND args " ${arg}")
  12. endif()
  13. endforeach()
  14. string(APPEND script "${name}(${test_name} ${args})\n")
  15. set(script "${script}" PARENT_SCOPE)
  16. endfunction()
  17. function(generate_testname_guards output open_guard_var close_guard_var)
  18. set(open_guard "[=[")
  19. set(close_guard "]=]")
  20. set(counter 1)
  21. while("${output}" MATCHES "${close_guard}")
  22. math(EXPR counter "${counter} + 1")
  23. string(REPEAT "=" ${counter} equals)
  24. set(open_guard "[${equals}[")
  25. set(close_guard "]${equals}]")
  26. endwhile()
  27. set(${open_guard_var} "${open_guard}" PARENT_SCOPE)
  28. set(${close_guard_var} "${close_guard}" PARENT_SCOPE)
  29. endfunction()
  30. function(escape_square_brackets output bracket placeholder placeholder_var output_var)
  31. if("${output}" MATCHES "\\${bracket}")
  32. set(placeholder "${placeholder}")
  33. while("${output}" MATCHES "${placeholder}")
  34. set(placeholder "${placeholder}_")
  35. endwhile()
  36. string(REPLACE "${bracket}" "${placeholder}" output "${output}")
  37. set(${placeholder_var} "${placeholder}" PARENT_SCOPE)
  38. set(${output_var} "${output}" PARENT_SCOPE)
  39. endif()
  40. endfunction()
  41. function(gtest_discover_tests_impl)
  42. set(options "")
  43. set(oneValueArgs
  44. NO_PRETTY_TYPES # These two take a value, unlike gtest_discover_tests()
  45. NO_PRETTY_VALUES #
  46. TEST_EXECUTABLE
  47. TEST_WORKING_DIR
  48. TEST_PREFIX
  49. TEST_SUFFIX
  50. TEST_LIST
  51. CTEST_FILE
  52. TEST_DISCOVERY_TIMEOUT
  53. TEST_XML_OUTPUT_DIR
  54. # The following are all multi-value arguments in gtest_discover_tests(),
  55. # but they are each given to us as a single argument. We parse them that
  56. # way to avoid problems with preserving empty list values and escaping.
  57. TEST_FILTER
  58. TEST_EXTRA_ARGS
  59. TEST_DISCOVERY_EXTRA_ARGS
  60. TEST_PROPERTIES
  61. TEST_EXECUTOR
  62. )
  63. set(multiValueArgs "")
  64. cmake_parse_arguments(PARSE_ARGV 0 arg
  65. "${options}" "${oneValueArgs}" "${multiValueArgs}"
  66. )
  67. set(prefix "${arg_TEST_PREFIX}")
  68. set(suffix "${arg_TEST_SUFFIX}")
  69. set(script)
  70. set(suite)
  71. set(tests)
  72. set(tests_buffer "")
  73. # If a file at ${arg_CTEST_FILE} already exists, we overwrite it.
  74. # For performance reasons, we write to this file in chunks, and this variable
  75. # is updated to APPEND after the first write.
  76. set(file_write_mode WRITE)
  77. if(arg_TEST_FILTER)
  78. set(filter "--gtest_filter=${arg_TEST_FILTER}")
  79. else()
  80. set(filter)
  81. endif()
  82. # CMP0178 has already been handled in gtest_discover_tests(), so we only need
  83. # to implement NEW behavior here. This means preserving empty arguments for
  84. # TEST_EXECUTOR. For OLD or WARN, gtest_discover_tests() already removed any
  85. # empty arguments.
  86. set(launcherArgs "")
  87. if(NOT "${arg_TEST_EXECUTOR}" STREQUAL "")
  88. list(JOIN arg_TEST_EXECUTOR "]==] [==[" launcherArgs)
  89. set(launcherArgs "[==[${launcherArgs}]==]")
  90. endif()
  91. # Run test executable to get list of available tests
  92. if(NOT EXISTS "${arg_TEST_EXECUTABLE}")
  93. message(FATAL_ERROR
  94. "Specified test executable does not exist.\n"
  95. " Path: '${arg_TEST_EXECUTABLE}'"
  96. )
  97. endif()
  98. set(discovery_extra_args "")
  99. if(NOT "${arg_TEST_DISCOVERY_EXTRA_ARGS}" STREQUAL "")
  100. list(JOIN arg_TEST_DISCOVERY_EXTRA_ARGS "]==] [==[" discovery_extra_args)
  101. set(discovery_extra_args "[==[${discovery_extra_args}]==]")
  102. endif()
  103. cmake_language(EVAL CODE
  104. "execute_process(
  105. COMMAND ${launcherArgs} [==[${arg_TEST_EXECUTABLE}]==] --gtest_list_tests ${filter} ${discovery_extra_args}
  106. WORKING_DIRECTORY [==[${arg_TEST_WORKING_DIR}]==]
  107. TIMEOUT ${arg_TEST_DISCOVERY_TIMEOUT}
  108. OUTPUT_VARIABLE output
  109. RESULT_VARIABLE result
  110. )"
  111. )
  112. if(NOT ${result} EQUAL 0)
  113. string(REPLACE "\n" "\n " output "${output}")
  114. if(arg_TEST_EXECUTOR)
  115. set(path "${arg_TEST_EXECUTOR} ${arg_TEST_EXECUTABLE}")
  116. else()
  117. set(path "${arg_TEST_EXECUTABLE}")
  118. endif()
  119. message(FATAL_ERROR
  120. "Error running test executable.\n"
  121. " Path: '${path}'\n"
  122. " Working directory: '${arg_TEST_WORKING_DIR}'\n"
  123. " Result: ${result}\n"
  124. " Output:\n"
  125. " ${output}\n"
  126. )
  127. endif()
  128. generate_testname_guards("${output}" open_guard close_guard)
  129. escape_square_brackets("${output}" "[" "__osb" open_sb output)
  130. escape_square_brackets("${output}" "]" "__csb" close_sb output)
  131. # Preserve semicolon in test-parameters
  132. string(REPLACE [[;]] [[\;]] output "${output}")
  133. string(REPLACE "\n" ";" output "${output}")
  134. # Parse output
  135. foreach(line ${output})
  136. # Skip header
  137. if(NOT line MATCHES "gtest_main\\.cc")
  138. # Do we have a module name or a test name?
  139. if(NOT line MATCHES "^ ")
  140. # Module; remove trailing '.' to get just the name...
  141. string(REGEX REPLACE "\\.( *#.*)?$" "" suite "${line}")
  142. if(line MATCHES "#")
  143. string(REGEX REPLACE "/[0-9].*" "" pretty_suite "${line}")
  144. if(NOT arg_NO_PRETTY_TYPES)
  145. string(REGEX REPLACE ".*/[0-9]+[ .#]+TypeParam = (.*)" "\\1" type_parameter "${line}")
  146. else()
  147. string(REGEX REPLACE ".*/([0-9]+)[ .#]+TypeParam = .*" "\\1" type_parameter "${line}")
  148. endif()
  149. set(test_name_template "@prefix@@pretty_suite@.@pretty_test@<@type_parameter@>@suffix@")
  150. else()
  151. set(pretty_suite "${suite}")
  152. set(test_name_template "@prefix@@pretty_suite@.@pretty_test@@suffix@")
  153. endif()
  154. string(REGEX REPLACE "^DISABLED_" "" pretty_suite "${pretty_suite}")
  155. else()
  156. string(STRIP "${line}" test)
  157. if(test MATCHES "#" AND NOT arg_NO_PRETTY_VALUES)
  158. string(REGEX REPLACE "/[0-9]+[ #]+GetParam\\(\\) = " "/" pretty_test "${test}")
  159. else()
  160. string(REGEX REPLACE " +#.*" "" pretty_test "${test}")
  161. endif()
  162. string(REGEX REPLACE "^DISABLED_" "" pretty_test "${pretty_test}")
  163. string(REGEX REPLACE " +#.*" "" test "${test}")
  164. if(NOT "${arg_TEST_XML_OUTPUT_DIR}" STREQUAL "")
  165. set(TEST_XML_OUTPUT_PARAM "--gtest_output=xml:${arg_TEST_XML_OUTPUT_DIR}/${prefix}${suite}.${test}${suffix}.xml")
  166. else()
  167. unset(TEST_XML_OUTPUT_PARAM)
  168. endif()
  169. string(CONFIGURE "${test_name_template}" testname)
  170. # unescape []
  171. if(open_sb)
  172. string(REPLACE "${open_sb}" "[" testname "${testname}")
  173. endif()
  174. if(close_sb)
  175. string(REPLACE "${close_sb}" "]" testname "${testname}")
  176. endif()
  177. set(guarded_testname "${open_guard}${testname}${close_guard}")
  178. # Add to script. Do not use add_command() here because it messes up the
  179. # handling of empty values when forwarding arguments, and we need to
  180. # preserve those carefully for arg_TEST_EXECUTOR and arg_EXTRA_ARGS.
  181. string(APPEND script "add_test(${guarded_testname} ${launcherArgs}")
  182. foreach(arg IN ITEMS
  183. "${arg_TEST_EXECUTABLE}"
  184. "--gtest_filter=${suite}.${test}"
  185. "--gtest_also_run_disabled_tests"
  186. ${TEST_XML_OUTPUT_PARAM}
  187. )
  188. if(arg MATCHES "[^-./:a-zA-Z0-9_]")
  189. string(APPEND script " [==[${arg}]==]")
  190. else()
  191. string(APPEND script " ${arg}")
  192. endif()
  193. endforeach()
  194. if(arg_TEST_EXTRA_ARGS)
  195. list(JOIN arg_TEST_EXTRA_ARGS "]==] [==[" extra_args)
  196. string(APPEND script " [==[${extra_args}]==]")
  197. endif()
  198. string(APPEND script ")\n")
  199. if(suite MATCHES "^DISABLED_" OR test MATCHES "^DISABLED_")
  200. add_command(set_tests_properties
  201. "${guarded_testname}"
  202. PROPERTIES DISABLED TRUE
  203. )
  204. endif()
  205. add_command(set_tests_properties
  206. "${guarded_testname}"
  207. PROPERTIES
  208. WORKING_DIRECTORY "${arg_TEST_WORKING_DIR}"
  209. SKIP_REGULAR_EXPRESSION "\\[ SKIPPED \\]"
  210. ${arg_TEST_PROPERTIES}
  211. )
  212. # possibly unbalanced square brackets render lists invalid so skip such
  213. # tests in ${arg_TEST_LIST}
  214. if(NOT "${testname}" MATCHES [=[(\[|\])]=])
  215. # escape ;
  216. string(REPLACE [[;]] [[\\;]] testname "${testname}")
  217. list(APPEND tests_buffer "${testname}")
  218. list(LENGTH tests_buffer tests_buffer_length)
  219. if(tests_buffer_length GREATER "250")
  220. # Chunk updates to the final "tests" variable, keeping the
  221. # "tests_buffer" variable that we append each test to relatively
  222. # small. This mitigates worsening performance impacts for the
  223. # corner case of having many thousands of tests.
  224. list(APPEND tests "${tests_buffer}")
  225. set(tests_buffer "")
  226. endif()
  227. endif()
  228. endif()
  229. # If we've built up a sizable script so far, write it out as a chunk now
  230. # so we don't accumulate a massive string to write at the end
  231. string(LENGTH "${script}" script_len)
  232. if(${script_len} GREATER "50000")
  233. file(${file_write_mode} "${arg_CTEST_FILE}" "${script}")
  234. set(file_write_mode APPEND)
  235. set(script "")
  236. endif()
  237. endif()
  238. endforeach()
  239. if(NOT tests_buffer STREQUAL "")
  240. list(APPEND tests "${tests_buffer}")
  241. endif()
  242. # Create a list of all discovered tests, which users may use to e.g. set
  243. # properties on the tests
  244. add_command(set "" ${arg_TEST_LIST} "${tests}")
  245. # Write remaining content to the CTest script
  246. file(${file_write_mode} "${arg_CTEST_FILE}" "${script}")
  247. endfunction()
  248. if(CMAKE_SCRIPT_MODE_FILE)
  249. gtest_discover_tests_impl(
  250. NO_PRETTY_TYPES ${NO_PRETTY_TYPES}
  251. NO_PRETTY_VALUES ${NO_PRETTY_VALUES}
  252. TEST_EXECUTABLE ${TEST_EXECUTABLE}
  253. TEST_EXECUTOR "${TEST_EXECUTOR}"
  254. TEST_WORKING_DIR ${TEST_WORKING_DIR}
  255. TEST_PREFIX ${TEST_PREFIX}
  256. TEST_SUFFIX ${TEST_SUFFIX}
  257. TEST_FILTER ${TEST_FILTER}
  258. TEST_LIST ${TEST_LIST}
  259. CTEST_FILE ${CTEST_FILE}
  260. TEST_DISCOVERY_TIMEOUT ${TEST_DISCOVERY_TIMEOUT}
  261. TEST_XML_OUTPUT_DIR ${TEST_XML_OUTPUT_DIR}
  262. TEST_EXTRA_ARGS "${TEST_EXTRA_ARGS}"
  263. TEST_DISCOVERY_EXTRA_ARGS "${TEST_DISCOVERY_EXTRA_ARGS}"
  264. TEST_PROPERTIES "${TEST_PROPERTIES}"
  265. )
  266. endif()