Skip to content

Commit 4517644

Browse files
Add cmake Modules for boosttest_discover_tests
1 parent efbbc01 commit 4517644

File tree

2 files changed

+524
-0
lines changed

2 files changed

+524
-0
lines changed

Modules/BoostTestAddTests.cmake

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2+
# file Copyright.txt or https://cmake.org/licensing for details.
3+
4+
cmake_minimum_required(VERSION ${CMAKE_VERSION})
5+
6+
# Overwrite possibly existing ${__CTEST_FILE} with empty file.
7+
set(flush_tests_MODE WRITE)
8+
9+
10+
# Flushes script to ${__CTEST_FILE}.
11+
macro(flush_script)
12+
file(${flush_tests_MODE} "${__CTEST_FILE}" "${script}")
13+
set(flush_tests_MODE APPEND)
14+
set(script "")
15+
endmacro()
16+
17+
18+
# Flushes tests_buffer to tests variable.
19+
macro(flush_tests_buffer)
20+
list(APPEND tests "${tests_buffer}")
21+
set(tests_buffer "")
22+
endmacro()
23+
24+
25+
# Removes surrounding double quotes (if any) from value of given variable VAR.
26+
function(remove_outer_quotes VAR)
27+
string(REGEX REPLACE "^\"(.*)\"$" "\\1" ${VAR} "${${VAR}}")
28+
set(${VAR} "${${VAR}}" PARENT_SCOPE)
29+
endfunction()
30+
31+
32+
# Adds commands to script.
33+
macro(add_command NAME)
34+
set(_args "")
35+
foreach(_arg ${ARGN})
36+
if(_arg MATCHES "[^-./:a-zA-Z0-9_]")
37+
string(APPEND _args " [==[${_arg}]==]") # form a bracket argument
38+
else()
39+
string(APPEND _args " ${_arg}")
40+
endif()
41+
endforeach()
42+
string(APPEND script "${NAME}(${_args})\n")
43+
string(LENGTH "${script}" _script_len)
44+
if(${_script_len} GREATER "50000")
45+
flush_script()
46+
endif()
47+
# Unsets macro local variables to prevent leakage outside of this macro.
48+
unset(_args)
49+
unset(_script_len)
50+
endmacro()
51+
52+
53+
# Adds another test to the script.
54+
macro(add_another_test hierarchy_list enabled separator)
55+
# Create the name and path of the test-case...
56+
list(JOIN ${hierarchy_list} ${separator} test_name)
57+
list(JOIN ${hierarchy_list} "/" test_path)
58+
# ...and add to script.
59+
add_command(add_test
60+
"${prefix}${test_name}${suffix}"
61+
${__TEST_EXECUTOR}
62+
"${__TEST_EXECUTABLE}"
63+
"--run_test=${test_path}"
64+
${extra_args}
65+
)
66+
if(NOT ${enabled})
67+
add_command(set_tests_properties
68+
"${prefix}${test_name}${suffix}"
69+
PROPERTIES DISABLED TRUE
70+
)
71+
endif()
72+
add_command(set_tests_properties
73+
"${prefix}${test_name}${suffix}"
74+
PROPERTIES
75+
WORKING_DIRECTORY "${__TEST_WORKING_DIR}"
76+
${properties}
77+
)
78+
list(APPEND tests_buffer "${prefix}${test_name}${suffix}")
79+
list(LENGTH tests_buffer tests_buffer_length)
80+
if(${tests_buffer_length} GREATER "250")
81+
flush_tests_buffer()
82+
endif()
83+
endmacro()
84+
85+
86+
# Internal implementation for boosttest_discover_tests.
87+
function(boosttest_discover_tests_impl)
88+
89+
cmake_parse_arguments(
90+
"_"
91+
""
92+
"TEST_EXECUTABLE;TEST_EXECUTOR;TEST_WORKING_DIR;TEST_PREFIX;TEST_SUFFIX;TEST_NAME_SEPARATOR;TEST_LIST;TEST_SKIP_DISABLED;CTEST_FILE;TEST_DISCOVERY_TIMEOUT"
93+
"TEST_EXTRA_ARGS;TEST_PROPERTIES"
94+
${ARGN}
95+
)
96+
97+
set(prefix "${__TEST_PREFIX}")
98+
set(suffix "${__TEST_SUFFIX}")
99+
set(extra_args ${__TEST_EXTRA_ARGS})
100+
set(properties ${__TEST_PROPERTIES})
101+
set(script)
102+
set(tests)
103+
set(tests_buffer)
104+
105+
# Make sure the working directory exists.
106+
file(MAKE_DIRECTORY "${__TEST_WORKING_DIR}")
107+
# Run test executable to get list of available tests.
108+
if(NOT EXISTS "${__TEST_EXECUTABLE}")
109+
message(FATAL_ERROR
110+
"Specified test executable does not exist.\n"
111+
" Path: '${__TEST_EXECUTABLE}'"
112+
)
113+
endif()
114+
execute_process(
115+
COMMAND ${__TEST_EXECUTOR} "${__TEST_EXECUTABLE}" --list_content=HRF
116+
WORKING_DIRECTORY "${__TEST_WORKING_DIR}"
117+
TIMEOUT ${__TEST_DISCOVERY_TIMEOUT}
118+
OUTPUT_VARIABLE output
119+
ERROR_VARIABLE output # Boost.Test writes the requested content to stderr!
120+
RESULT_VARIABLE result
121+
)
122+
if(NOT ${result} EQUAL 0)
123+
string(REPLACE "\n" "\n " output "${output}")
124+
message(FATAL_ERROR
125+
"Error running test executable.\n"
126+
" Path: '${__TEST_EXECUTABLE}'\n"
127+
" Result: ${result}\n"
128+
" Output:\n"
129+
" ${output}\n"
130+
)
131+
endif()
132+
133+
# Preserve semicolon in test-parameters
134+
string(REPLACE [[;]] [[\;]] output "${output}")
135+
string(REPLACE "\n" ";" output "${output}")
136+
137+
# The hierarchy and its depth-level of the test of the former line.
138+
set(hierarchy "")
139+
set(former_level NaN)
140+
set(test_is_enabled 1)
141+
142+
# Parse output
143+
foreach(line ${output})
144+
# Determine the depth-level of the next test-hierarchy.
145+
# Note: Each new depth-level (except for the top one) is indented
146+
# by 4 spaces. So we need to count the spaces.
147+
string(REGEX MATCH "^[ ]+" next_level "${line}")
148+
string(LENGTH "${next_level}" next_level)
149+
math(EXPR next_level "${next_level} / 4")
150+
151+
# Add the test for the test-case from the former loop-run?
152+
if (next_level LESS_EQUAL former_level)
153+
# Add test-case to the script.
154+
add_another_test(hierarchy ${test_is_enabled} "${__TEST_NAME_SEPARATOR}")
155+
156+
# Prepare the hierarchy list for the next test-case.
157+
math(EXPR diff "${former_level} - ${next_level}")
158+
foreach(i RANGE ${diff})
159+
list(POP_BACK hierarchy)
160+
endforeach()
161+
endif()
162+
set(former_level ${next_level}) # Store depth-level for next loop-run.
163+
164+
# Extract the name of the next test suite/case and determine if enabled.
165+
# Note: A trailing '*' indicates that the test is enabled (by default).
166+
string(REGEX REPLACE ":( .*)?$" "" name "${line}")
167+
string(STRIP "${name}" name)
168+
if(name MATCHES "\\*$")
169+
set(test_is_enabled 1)
170+
string(REGEX REPLACE "\\*$" "" name "${name}")
171+
elseif(__TEST_SKIP_DISABLED)
172+
set(test_is_enabled 0)
173+
endif()
174+
175+
# Sanitize name for further processing downstream:
176+
# - escape \
177+
string(REPLACE [[\]] [[\\]] name "${name}")
178+
# - escape ;
179+
string(REPLACE [[;]] [[\;]] name "${name}")
180+
# - escape $
181+
string(REPLACE [[$]] [[\$]] name "${name}")
182+
183+
# Add the name to the hierarchy list.
184+
list(APPEND hierarchy "${name}")
185+
endforeach()
186+
187+
# Add last test-case to the script.
188+
add_another_test(hierarchy ${test_is_enabled} "${__TEST_NAME_SEPARATOR}")
189+
190+
191+
# Create a list of all discovered tests, which users may use to e.g. set
192+
# properties on the tests.
193+
flush_tests_buffer()
194+
add_command(set ${__TEST_LIST} ${tests})
195+
196+
# Write CTest script
197+
file(WRITE "${__CTEST_FILE}" "${script}")
198+
199+
# Write CTest script
200+
flush_script()
201+
202+
endfunction()
203+
204+
if(CMAKE_SCRIPT_MODE_FILE)
205+
# Note: Make sure to remove the outer layer of quotes that were added
206+
# to preserve whitespace when handed over via cmdline.
207+
remove_outer_quotes(TEST_TARGET)
208+
remove_outer_quotes(TEST_EXECUTABLE)
209+
remove_outer_quotes(TEST_EXECUTOR)
210+
remove_outer_quotes(TEST_WORKING_DIR)
211+
remove_outer_quotes(TEST_EXTRA_ARGS)
212+
remove_outer_quotes(TEST_PROPERTIES)
213+
remove_outer_quotes(TEST_PREFIX)
214+
remove_outer_quotes(TEST_SUFFIX)
215+
remove_outer_quotes(TEST_NAME_SEPARATOR)
216+
remove_outer_quotes(TEST_LIST)
217+
remove_outer_quotes(TEST_SKIP_DISABLED)
218+
remove_outer_quotes(CTEST_FILE)
219+
remove_outer_quotes(TEST_DISCOVERY_TIMEOUT)
220+
221+
boosttest_discover_tests_impl(
222+
TEST_EXECUTABLE ${TEST_EXECUTABLE}
223+
TEST_EXECUTOR ${TEST_EXECUTOR}
224+
TEST_WORKING_DIR ${TEST_WORKING_DIR}
225+
TEST_PREFIX ${TEST_PREFIX}
226+
TEST_SUFFIX ${TEST_SUFFIX}
227+
TEST_NAME_SEPARATOR ${TEST_NAME_SEPARATOR}
228+
TEST_LIST ${TEST_LIST}
229+
TEST_SKIP_DISABLED ${TEST_SKIP_DISABLED}
230+
CTEST_FILE ${CTEST_FILE}
231+
TEST_DISCOVERY_TIMEOUT ${TEST_DISCOVERY_TIMEOUT}
232+
TEST_EXTRA_ARGS ${TEST_EXTRA_ARGS}
233+
TEST_PROPERTIES ${TEST_PROPERTIES}
234+
)
235+
endif()

0 commit comments

Comments
 (0)