-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathPytestAddTests.cmake
More file actions
159 lines (127 loc) · 5.82 KB
/
PytestAddTests.cmake
File metadata and controls
159 lines (127 loc) · 5.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# Wrapper used to create individual CTest tests from Pytest tests.
cmake_minimum_required(VERSION 3.20...4.1)
if(CMAKE_SCRIPT_MODE_FILE)
# Initialize content for the CMake test file.
set(_content "")
# Convert library and Python paths to native format.
cmake_path(CONVERT "${LIBRARY_PATH}" TO_NATIVE_PATH_LIST LIBRARY_PATH)
cmake_path(CONVERT "${PYTHON_PATH}" TO_NATIVE_PATH_LIST PYTHON_PATH)
# Serialize path values separated by semicolons (required on Windows).
string(REPLACE [[;]] [[\\;]] LIBRARY_PATH "${LIBRARY_PATH}")
string(REPLACE [[;]] [[\\;]] PYTHON_PATH "${PYTHON_PATH}")
# Set up the encoded environment with required paths.
set(ENCODED_ENVIRONMENT
"${LIBRARY_ENV_NAME}=${LIBRARY_PATH}"
"PYTHONPATH=${PYTHON_PATH}"
)
# Serialize additional environment variables if any are provided.
foreach(env ${ENVIRONMENT})
string(REPLACE [[;]] [[\\;]] env "${env}")
list(APPEND ENCODED_ENVIRONMENT "${env}")
endforeach()
# Handle EXTRA_ARGS for individual tests
if(BUNDLE_TESTS)
list(PREPEND EXTRA_ARGS ${DISCOVERY_EXTRA_ARGS})
endif()
set(EXTRA_ARGS_WRAPPED)
foreach(arg IN LISTS EXTRA_ARGS)
list(APPEND EXTRA_ARGS_WRAPPED "[==[${arg}]==]")
endforeach()
list(JOIN EXTRA_ARGS_WRAPPED " " EXTRA_ARGS_STR)
# Macro to create individual tests with optional test properties.
macro(create_test NAME IDENTIFIER)
string(APPEND _content
"add_test([==[${NAME}]==] \"${PYTEST_EXECUTABLE}\" [==[${IDENTIFIER}]==] ${EXTRA_ARGS_STR} )\n"
)
# Prepare the properties for the test, including the environment settings.
set(args "PROPERTIES ENVIRONMENT [==[${ENCODED_ENVIRONMENT}]==]")
# Append any additional properties, escaping complex characters if necessary.
foreach(property ${TEST_PROPERTIES})
if(property MATCHES "[^-./:a-zA-Z0-9_]")
string(APPEND args " [==[${property}]==]")
else()
string(APPEND args " ${property}")
endif()
endforeach()
# Append the test properties to the content.
string(APPEND _content "set_tests_properties([==[${NAME}]==] ${args})\n")
endmacro()
# If tests are bundled together, create a single test group.
if (BUNDLE_TESTS)
create_test("\${TEST_GROUP_NAME}" "\${WORKING_DIRECTORY}")
else()
# Set environment variables for collecting tests.
set(ENV{${LIBRARY_ENV_NAME}} "${LIBRARY_PATH}")
set(ENV{PYTHONPATH} "${PYTHON_PATH}")
set(ENV{PYTHONWARNINGS} "ignore")
# Collect tests.
execute_process(
COMMAND "${PYTEST_EXECUTABLE}"
--collect-only -q
--rootdir=${WORKING_DIRECTORY} ${DISCOVERY_EXTRA_ARGS} .
OUTPUT_VARIABLE _output_lines
ERROR_VARIABLE _output_lines
OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${WORKING_DIRECTORY}
)
# Check for errors during test collection.
string(REGEX MATCH "=+ ERRORS =+(.*)" _error "${_output_lines}")
if (_error)
message(${_error})
message(FATAL_ERROR "An error occurred during the collection of Python tests.")
endif()
# Convert the collected output into a list of lines.
string(REPLACE [[;]] [[\;]] _output_lines "${_output_lines}")
string(REPLACE "\n" ";" _output_lines "${_output_lines}")
# Regex pattern to identify pytest test identifiers.
set(test_pattern "([^:]+)\.py(::([^:]+))?::([^:]+)")
# Iterate through each line to identify and process tests.
foreach(line ${_output_lines})
string(REGEX MATCHALL ${test_pattern} matching "${line}")
# Skip lines that are not identified as tests.
if (NOT matching)
continue()
endif()
# Extract file, class, and function names from the test pattern.
set(_file ${CMAKE_MATCH_1})
set(_class ${CMAKE_MATCH_3})
set(_func ${CMAKE_MATCH_4})
# Optionally trim parts of the class or function name.
if (TRIM_FROM_NAME)
string(REGEX REPLACE "${TRIM_FROM_NAME}" "" _class "${_class}")
string(REGEX REPLACE "${TRIM_FROM_NAME}" "" _func "${_func}")
endif()
# Form the test name using class and function.
if (_class)
set(test_name "${_class}.${_func}")
else()
set(test_name "${_func}")
endif()
# Optionally strip parameter brackets from the test name.
if (STRIP_PARAM_BRACKETS)
string(REGEX REPLACE "\\[(.+)\\]$" ".\\1" test_name "${test_name}")
endif()
# Optionally include the file path in the test name.
if (INCLUDE_FILE_PATH)
cmake_path(CONVERT "${_file}" TO_CMAKE_PATH_LIST _file)
string(REGEX REPLACE "/" "." _file "${_file}")
set(test_name "${_file}.${test_name}")
endif()
# Optionally trim parts of the full test name.
if (TRIM_FROM_FULL_NAME)
string(REGEX REPLACE "${TRIM_FROM_FULL_NAME}" "" test_name "${test_name}")
endif()
# Prefix the test name with the test group name.
set(test_name "${TEST_GROUP_NAME}.${test_name}")
set(test_case "${WORKING_DIRECTORY}/${line}")
# Create the test for CTest.
create_test("\${test_name}" "\${test_case}")
endforeach()
# Warn if no tests were discovered.
if(NOT _content)
message(WARNING "No Python tests have been discovered.")
endif()
endif()
# Write the generated test content to the specified CTest file.
file(WRITE ${CTEST_FILE} ${_content})
endif()