Skip to content

Commit c7b8016

Browse files
committed
Better integration of boost.yest and CTest allowing parallel test execution
1 parent 2edecd3 commit c7b8016

4 files changed

Lines changed: 605 additions & 8 deletions

File tree

CMakeLists.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON)
99
set(CMAKE_CXX_EXTENSIONS OFF)
1010
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
1111

12-
set(CMAKE_MODULE_PATH ${CMAKE_BINARY_DIR} ${CMAKE_MODULE_PATH})
12+
set(CMAKE_MODULE_PATH
13+
"${CMAKE_CURRENT_SOURCE_DIR}/cmake"
14+
${CMAKE_BINARY_DIR}
15+
${CMAKE_MODULE_PATH}
16+
)
1317
set(CMAKE_PREFIX_PATH ${CMAKE_BINARY_DIR} ${CMAKE_PREFIX_PATH})
1418

1519
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/out/lib)

cmake/BoostTestAddTests.cmake

Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
# Copyright 2020, 2021 Deniz Bahadir and contributors
2+
# Distributed under the Boost Software License, Version 1.0.
3+
# See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt
4+
#
5+
# This code is based on an implementation of the `gtest_discover_tests()`
6+
# functionality that was originally distributed together with CMake unter the
7+
# OSI-approved BSD 3-Clause License. The original authors and contributors of
8+
# that code were so kind to agree to dual-license their work also under the
9+
# Boost Software License, Version 1.0, so that this derived worked can be
10+
# distributed under the same license as well.
11+
# A public record of their granted permission can be found in this discussion
12+
# of merge-request 4145 in the CMake issue tracker:
13+
# https://gitlab.kitware.com/cmake/cmake/-/merge_requests/4145#note_998500
14+
#
15+
# Therefore many thanks go to the following original authors and contributors:
16+
# Matthew Woehlke <matthew.woehlke@kitware.com>
17+
# Steffen Seckler <steffen.seckler@smartronic.de>
18+
# Ryan Thornton <ThorntonRyan@JohnDeere.com>
19+
# Kevin Puetz <PuetzKevinA@JohnDeere.com>
20+
# Stefan Floeren <stefan.floeren@smartronic.de>
21+
# Alexander Stein <alexander.stein@mailbox.org>
22+
#
23+
# Deniz Bahadir in August of 2021.
24+
#
25+
26+
cmake_minimum_required(VERSION ${CMAKE_VERSION})
27+
28+
set(flush_tests_MODE WRITE)
29+
30+
31+
macro(flush_script)
32+
file(${flush_tests_MODE} "${__CTEST_FILE}" "${script}")
33+
set(flush_tests_MODE APPEND)
34+
set(script "")
35+
endmacro()
36+
37+
38+
macro(flush_tests_buffer)
39+
list(APPEND tests ${tests_buffer})
40+
set(tests_buffer "")
41+
endmacro()
42+
43+
44+
function(remove_outer_quotes VAR)
45+
string(REGEX REPLACE "^\"(.*)\"$" "\\1" ${VAR} "${${VAR}}")
46+
set(${VAR} "${${VAR}}" PARENT_SCOPE)
47+
endfunction()
48+
49+
50+
macro(add_command NAME)
51+
set(_args "")
52+
foreach(_arg ${ARGN})
53+
if(_arg MATCHES "[^-./:a-zA-Z0-9_]")
54+
string(APPEND _args " [==[${_arg}]==]")
55+
else()
56+
string(APPEND _args " ${_arg}")
57+
endif()
58+
endforeach()
59+
string(APPEND script "${NAME}(${_args})\n")
60+
string(LENGTH "${script}" _script_len)
61+
if(${_script_len} GREATER "50000")
62+
flush_script()
63+
endif()
64+
unset(_args)
65+
unset(_script_len)
66+
endmacro()
67+
68+
69+
macro(add_another_test hierarchy_list enabled source_line separator)
70+
if(CMAKE_VERSION VERSION_LESS "3.12")
71+
set(test_name)
72+
set(test_path)
73+
foreach(hierarchy_entry IN LISTS ${hierarchy_list})
74+
if("${test_name}" STREQUAL "")
75+
set(test_name "${hierarchy_entry}")
76+
else()
77+
set(test_name "${test_name}${separator}${hierarchy_entry}")
78+
endif()
79+
if("${test_path}" STREQUAL "")
80+
set(test_path "${hierarchy_entry}")
81+
else()
82+
set(test_path "${test_path}/${hierarchy_entry}")
83+
endif()
84+
endforeach()
85+
else()
86+
list(JOIN ${hierarchy_list} ${separator} test_name)
87+
list(JOIN ${hierarchy_list} "/" test_path)
88+
endif()
89+
90+
add_command(add_test
91+
"${prefix}${test_name}${suffix}"
92+
${__TEST_EXECUTOR}
93+
"${__TEST_EXECUTABLE}"
94+
"--run_test=${test_path}"
95+
${extra_args}
96+
)
97+
if(NOT ${enabled})
98+
add_command(set_tests_properties
99+
"${prefix}${test_name}${suffix}"
100+
PROPERTIES
101+
DISABLED TRUE
102+
)
103+
endif()
104+
add_command(set_tests_properties
105+
"${prefix}${test_name}${suffix}"
106+
PROPERTIES
107+
WORKING_DIRECTORY "${__TEST_WORKING_DIR}"
108+
DEF_SOURCE_LINE "${source_line}"
109+
${properties}
110+
)
111+
list(APPEND tests_buffer "${prefix}${test_name}${suffix}")
112+
list(LENGTH tests_buffer tests_buffer_length)
113+
if(${tests_buffer_length} GREATER "250")
114+
flush_tests_buffer()
115+
endif()
116+
endmacro()
117+
118+
119+
function(boost_test_discover_tests_impl)
120+
cmake_parse_arguments(
121+
"_"
122+
""
123+
"TEST_TARGET;TEST_EXECUTABLE;TEST_WORKING_DIR;TEST_PREFIX;TEST_SUFFIX;TEST_NAME_SEPARATOR;TEST_LIST;TEST_SKIP_DISABLED;CTEST_FILE;TEST_DISCOVERY_TIMEOUT"
124+
"TEST_EXTRA_ARGS;TEST_PROPERTIES;TEST_EXECUTOR"
125+
${ARGN}
126+
)
127+
128+
set(prefix "${__TEST_PREFIX}")
129+
set(suffix "${__TEST_SUFFIX}")
130+
set(extra_args ${__TEST_EXTRA_ARGS})
131+
set(properties ${__TEST_PROPERTIES})
132+
set(script)
133+
set(tests)
134+
set(tests_buffer)
135+
136+
file(MAKE_DIRECTORY "${__TEST_WORKING_DIR}")
137+
if(NOT EXISTS "${__TEST_EXECUTABLE}")
138+
message(FATAL_ERROR
139+
"Specified test executable does not exist.\n"
140+
" Path: '${__TEST_EXECUTABLE}'"
141+
)
142+
endif()
143+
144+
execute_process(
145+
COMMAND ${__TEST_EXECUTOR} "${__TEST_EXECUTABLE}" --list_content=DOT --detect_memory_leaks=0
146+
WORKING_DIRECTORY "${__TEST_WORKING_DIR}"
147+
TIMEOUT ${__TEST_DISCOVERY_TIMEOUT}
148+
OUTPUT_VARIABLE dot_output
149+
ERROR_VARIABLE dot_output
150+
RESULT_VARIABLE result
151+
)
152+
153+
if(NOT ${result} EQUAL 0)
154+
message(FATAL_ERROR
155+
"Error running test executable.\n"
156+
" Path: '${__TEST_EXECUTABLE}'\n"
157+
" Result: ${result}\n"
158+
" Output:\n"
159+
" ${dot_output}\n"
160+
)
161+
endif()
162+
163+
string(REPLACE [[\]] [[/]] dot_output "${dot_output}")
164+
165+
execute_process(
166+
COMMAND ${__TEST_EXECUTOR} "${__TEST_EXECUTABLE}" --list_content=HRF --detect_memory_leaks=0
167+
WORKING_DIRECTORY "${__TEST_WORKING_DIR}"
168+
TIMEOUT ${__TEST_DISCOVERY_TIMEOUT}
169+
OUTPUT_VARIABLE hrf_output
170+
ERROR_VARIABLE hrf_output
171+
RESULT_VARIABLE result
172+
)
173+
174+
if(NOT ${result} EQUAL 0)
175+
string(REPLACE "\n" "\n " hrf_output "${hrf_output}")
176+
message(FATAL_ERROR
177+
"Error running test executable.\n"
178+
" Path: '${__TEST_EXECUTABLE}'\n"
179+
" Result: ${result}\n"
180+
" Output:\n"
181+
" ${hrf_output}\n"
182+
)
183+
endif()
184+
185+
string(REPLACE [[;]] [[\;]] hrf_output "${hrf_output}")
186+
string(REPLACE "\n" ";" hrf_output "${hrf_output}")
187+
188+
set(test_hierarchy "${__TEST_TARGET}_MISSING_TESTS")
189+
set(former_level NaN)
190+
set(test_enabled 0)
191+
set(test_source_line "")
192+
193+
foreach(line ${hrf_output})
194+
string(REGEX MATCH "^[ ]+" next_level "${line}")
195+
string(LENGTH "${next_level}" next_level)
196+
math(EXPR next_level "${next_level} / 4")
197+
198+
if((next_level LESS former_level) OR (next_level EQUAL former_level))
199+
add_another_test(test_hierarchy ${test_enabled} "${test_source_line}" "${__TEST_NAME_SEPARATOR}")
200+
201+
math(EXPR diff "${former_level} - ${next_level}")
202+
foreach(i RANGE ${diff})
203+
if(CMAKE_VERSION VERSION_LESS "3.15")
204+
list(LENGTH test_hierarchy length)
205+
math(EXPR index "${length} - 1")
206+
list(REMOVE_AT test_hierarchy ${index})
207+
else()
208+
list(POP_BACK test_hierarchy)
209+
endif()
210+
endforeach()
211+
endif()
212+
if(former_level STREQUAL NaN)
213+
set(test_hierarchy "")
214+
set(test_source_line "")
215+
endif()
216+
set(former_level ${next_level})
217+
218+
string(REGEX REPLACE ":( .*)?$" "" name "${line}")
219+
string(STRIP "${name}" name)
220+
if(name MATCHES "\\*$")
221+
set(test_enabled 1)
222+
string(REGEX REPLACE "\\*$" "" name "${name}")
223+
elseif(__TEST_SKIP_DISABLED)
224+
set(test_enabled 0)
225+
endif()
226+
227+
string(REGEX REPLACE [[([\;$])]] [[\\\1]] name "${name}")
228+
229+
string(REGEX MATCH "${name}\|[^\"]+" test_source_line "${dot_output}")
230+
string(REGEX REPLACE "${name}\|([^\\(]+)\\(([0-9]+)\\)" "\\1:\\2" test_source_line "${test_source_line}")
231+
232+
list(APPEND test_hierarchy "${name}")
233+
endforeach()
234+
235+
add_another_test(test_hierarchy ${test_enabled} "${test_source_line}" "${__TEST_NAME_SEPARATOR}")
236+
237+
flush_tests_buffer()
238+
if(NOT former_level STREQUAL "NaN")
239+
add_command(set ${__TEST_LIST} ${tests})
240+
endif()
241+
242+
flush_script()
243+
endfunction()
244+
245+
246+
if(CMAKE_SCRIPT_MODE_FILE)
247+
remove_outer_quotes(TEST_TARGET)
248+
remove_outer_quotes(TEST_EXECUTABLE)
249+
remove_outer_quotes(TEST_EXECUTOR)
250+
remove_outer_quotes(TEST_WORKING_DIR)
251+
remove_outer_quotes(TEST_EXTRA_ARGS)
252+
remove_outer_quotes(TEST_PROPERTIES)
253+
remove_outer_quotes(TEST_PREFIX)
254+
remove_outer_quotes(TEST_SUFFIX)
255+
remove_outer_quotes(TEST_NAME_SEPARATOR)
256+
remove_outer_quotes(TEST_LIST)
257+
remove_outer_quotes(TEST_SKIP_DISABLED)
258+
remove_outer_quotes(CTEST_FILE)
259+
remove_outer_quotes(TEST_DISCOVERY_TIMEOUT)
260+
261+
boost_test_discover_tests_impl(
262+
TEST_TARGET ${TEST_TARGET}
263+
TEST_EXECUTABLE ${TEST_EXECUTABLE}
264+
TEST_EXECUTOR ${TEST_EXECUTOR}
265+
TEST_WORKING_DIR ${TEST_WORKING_DIR}
266+
TEST_PREFIX ${TEST_PREFIX}
267+
TEST_SUFFIX ${TEST_SUFFIX}
268+
TEST_NAME_SEPARATOR ${TEST_NAME_SEPARATOR}
269+
TEST_LIST ${TEST_LIST}
270+
TEST_SKIP_DISABLED ${TEST_SKIP_DISABLED}
271+
CTEST_FILE ${CTEST_FILE}
272+
TEST_DISCOVERY_TIMEOUT ${TEST_DISCOVERY_TIMEOUT}
273+
TEST_EXTRA_ARGS ${TEST_EXTRA_ARGS}
274+
TEST_PROPERTIES ${TEST_PROPERTIES}
275+
)
276+
endif()

0 commit comments

Comments
 (0)