1# Copyright 2022 The Pigweed Authors 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); you may not 4# use this file except in compliance with the License. You may obtain a copy of 5# the License at 6# 7# https://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12# License for the specific language governing permissions and limitations under 13# the License. 14include_guard(GLOBAL) 15 16include($ENV{PW_ROOT}/pw_build/pigweed.cmake) 17 18set(pw_unit_test_ENABLE_PW_ADD_TEST ON CACHE BOOL 19 "Enable or disable pw_add_test calls. This is useful if you would like to \ 20 disable test generation when adding Pigweed to an existing project. Set to \ 21 OFF before the add_subdirectory(third_party/pigweed) call to prevent tests \ 22 from being generated.") 23 24set(pw_unit_test_BACKEND pw_unit_test.light CACHE STRING 25 "CMake target which implements GoogleTest, by default pw_unit_test.light \ 26 is used. You could, for example, point this at pw_unit_test.googletest \ 27 if using upstream GoogleTest directly on your host for GoogleMock.") 28 29# TODO(ewout): Remove the default. 30set(pw_unit_test_ADD_EXECUTABLE_FUNCTION "pw_add_test_executable" CACHE STRING 31 "The name of the CMake function used to instantiate pw_unit_test \ 32 executables") 33 34# TODO(ewout): Remove the default. 35set(pw_unit_test_ADD_EXECUTABLE_FUNCTION_FILE 36 "$ENV{PW_ROOT}/targets/host/pw_add_test_executable.cmake" CACHE STRING 37 "The path to the .cmake file that defines \ 38 pw_unit_test_ADD_EXECUTABLE_FUNCTION.") 39 40# TODO(ewout): Remove the default to match GN and support Windows. 41set(pw_unit_test_AUTOMATIC_RUNNER "$ENV{PW_ROOT}/targets/host/run_test" CACHE 42 STRING 43 "Path to a test runner to automatically run unit tests after they are \ 44 built. \ 45 If set, a pw_add_test's {NAME}.run action will invoke the test runner \ 46 specified by this variable, passing the path to the unit test to run. If \ 47 set to an empty string, the {NAME}.run step will fail to build.") 48 49set(pw_unit_test_AUTOMATIC_RUNNER_TIMEOUT_SECONDS "" CACHE STRING 50 "Optional timeout to apply when running tests via the automatic runner. \ 51 Timeout is in seconds. Defaults to empty which means no timeout.") 52 53set(pw_unit_test_AUTOMATIC_RUNNER_ARGS "" CACHE STRING 54 "Optional list of arguments to forward to the automatic runner") 55 56# pw_add_test: Declares a single unit test suite with Pigweed naming rules and 57# compiler warning options. 58# 59# {NAME} depends on ${NAME}.run if pw_unit_test_AUTOMATIC_RUNNER is set, else 60# it depends on ${NAME}.bin 61# {NAME}.lib contains the provided test sources as a library target, which can 62# then be linked into a test executable. 63# {NAME}.bin is a standalone executable which contains only the test sources 64# specified in the pw_unit_test_template. 65# {NAME}.run which runs the unit test executable after building it if 66# pw_unit_test_AUTOMATIC_RUNNER is set, else it fails to build. 67# 68# Required Arguments: 69# 70# NAME: name to use for the produced test targets specified above 71# 72# Optional Arguments: 73# 74# SOURCES - source files for this library 75# HEADERS - header files for this library 76# PRIVATE_DEPS - private pw_target_link_targets arguments 77# PRIVATE_INCLUDES - public target_include_directories argument 78# PRIVATE_DEFINES - private target_compile_definitions arguments 79# PRIVATE_COMPILE_OPTIONS - private target_compile_options arguments 80# PRIVATE_LINK_OPTIONS - private target_link_options arguments 81# 82# TODO(ewout, hepler): Deprecate the following legacy arguments 83# GROUPS - groups to which to add this test. 84# 85function(pw_add_test NAME) 86if("${pw_unit_test_ENABLE_PW_ADD_TEST}") 87 pw_parse_arguments( 88 NUM_POSITIONAL_ARGS 89 1 90 MULTI_VALUE_ARGS 91 SOURCES HEADERS PRIVATE_DEPS PRIVATE_INCLUDES 92 PRIVATE_DEFINES PRIVATE_COMPILE_OPTIONS 93 PRIVATE_LINK_OPTIONS GROUPS 94 ) 95 96 _pw_check_name_is_relative_to_root("${NAME}" "$ENV{PW_ROOT}" 97 REMAP_PREFIXES 98 third_party pw_third_party 99 ) 100 101 pw_add_test_generic(${NAME} 102 SOURCES 103 ${arg_SOURCES} 104 HEADERS 105 ${arg_HEADERS} 106 PRIVATE_DEPS 107 # TODO: b/232141950 - Apply compilation options that affect ABI 108 # globally in the CMake build instead of injecting them into libraries. 109 pw_build 110 ${arg_PRIVATE_DEPS} 111 PRIVATE_INCLUDES 112 ${arg_PRIVATE_INCLUDES} 113 PRIVATE_DEFINES 114 ${arg_PRIVATE_DEFINES} 115 PRIVATE_COMPILE_OPTIONS_DEPS_BEFORE 116 pw_build.warnings 117 PRIVATE_COMPILE_OPTIONS 118 ${arg_PRIVATE_COMPILE_OPTIONS} 119 PRIVATE_LINK_OPTIONS 120 ${arg_PRIVATE_LINK_OPTIONS} 121 GROUPS 122 ${arg_GROUPS} 123 ) 124endif() 125endfunction() 126 127# pw_add_test_generic: Declares a single unit test suite. 128# 129# {NAME} depends on ${NAME}.run if pw_unit_test_AUTOMATIC_RUNNER is set, else 130# it depends on ${NAME}.bin 131# {NAME}.lib contains the provided test sources as a library target, which can 132# then be linked into a test executable. 133# {NAME}.bin is a standalone executable which contains only the test sources 134# specified in the pw_unit_test_template. 135# {NAME}.run which runs the unit test executable after building it if 136# pw_unit_test_AUTOMATIC_RUNNER is set, else it fails to build. 137# 138# Required Arguments: 139# 140# NAME: name to use for the produced test targets specified above 141# 142# Optional Arguments: 143# 144# SOURCES - source files for this library 145# HEADERS - header files for this library 146# PRIVATE_DEPS - private pw_target_link_targets arguments 147# PRIVATE_INCLUDES - public target_include_directories argument 148# PRIVATE_DEFINES - private target_compile_definitions arguments 149# PRIVATE_COMPILE_OPTIONS_DEPS_BEFORE - private target_compile_options BEFORE 150# arguments from the specified deps's INTERFACE_COMPILE_OPTIONS. Note that 151# these deps are not pulled in as target_link_libraries. This should not be 152# exposed by the non-generic API. 153# PRIVATE_COMPILE_OPTIONS - private target_compile_options arguments 154# PRIVATE_LINK_OPTIONS - private target_link_options arguments 155# TEST_MAIN - overrides the default test main dependency 156# 157# TODO(ewout, hepler): Deprecate the following legacy arguments 158# GROUPS - groups to which to add this test. 159# 160function(pw_add_test_generic NAME) 161 pw_parse_arguments( 162 NUM_POSITIONAL_ARGS 163 1 164 ONE_VALUE_ARGS 165 TEST_MAIN 166 MULTI_VALUE_ARGS 167 SOURCES HEADERS PRIVATE_DEPS PRIVATE_INCLUDES 168 PRIVATE_DEFINES 169 PRIVATE_COMPILE_OPTIONS_DEPS_BEFORE PRIVATE_COMPILE_OPTIONS 170 PRIVATE_LINK_OPTIONS GROUPS 171 ) 172 173 # Add the library target under "${NAME}.lib". 174 # OBJECT libraries require at least one source file. 175 if("${arg_SOURCES}" STREQUAL "") 176 set(lib_type "INTERFACE") 177 else() 178 set(lib_type "OBJECT") 179 endif() 180 pw_add_library_generic("${NAME}.lib" ${lib_type} 181 SOURCES 182 ${arg_SOURCES} 183 HEADERS 184 ${arg_HEADERS} 185 PRIVATE_DEPS 186 pw_unit_test 187 ${arg_PRIVATE_DEPS} 188 PRIVATE_INCLUDES 189 ${arg_PRIVATE_INCLUDES} 190 PRIVATE_DEFINES 191 ${arg_PRIVATE_DEFINES} 192 PRIVATE_COMPILE_OPTIONS_DEPS_BEFORE 193 ${arg_PRIVATE_COMPILE_OPTIONS_DEPS_BEFORE} 194 PRIVATE_COMPILE_OPTIONS 195 ${arg_PRIVATE_COMPILE_OPTIONS} 196 PRIVATE_LINK_OPTIONS 197 ${arg_PRIVATE_LINK_OPTIONS} 198 ) 199 200 # Add the executable target under "${NAME}.bin". 201 if(("${pw_unit_test_ADD_EXECUTABLE_FUNCTION}" STREQUAL "") OR 202 ("${pw_unit_test_ADD_EXECUTABLE_FUNCTION_FILE}" STREQUAL "")) 203 pw_add_error_target("${NAME}.bin" 204 MESSAGE 205 "Attempted to build the ${NAME}.bin without enabling the unit " 206 "test executable function via pw_unit_test_ADD_EXECUTABLE_FUNCTION " 207 "and pw_unit_test_ADD_EXECUTABLE_FUNCTION_FILE. " 208 "See https://pigweed.dev/pw_unit_test for more details." 209 ) 210 else() 211 include("${pw_unit_test_ADD_EXECUTABLE_FUNCTION_FILE}") 212 if ("${arg_TEST_MAIN}" STREQUAL "") 213 cmake_language(CALL "${pw_unit_test_ADD_EXECUTABLE_FUNCTION}" 214 "${NAME}.bin" "${NAME}.lib") 215 else() 216 cmake_language(CALL "${pw_unit_test_ADD_EXECUTABLE_FUNCTION}_with_main" 217 "${NAME}.bin" "${NAME}.lib" "${arg_TEST_MAIN}") 218 endif() 219 endif() 220 221 # Add the ${NAME} target and optionally the run target under ${NAME}.run. 222 add_custom_target("${NAME}") 223 if("${pw_unit_test_AUTOMATIC_RUNNER}" STREQUAL "") 224 # Test runner is not provided, only build the executable. 225 add_dependencies("${NAME}" "${NAME}.bin") 226 227 pw_add_error_target("${NAME}.run" 228 MESSAGE 229 "Attempted to build ${NAME}.run which is not available because " 230 "pw_unit_test_AUTOMATIC_RUNNER has not been configured. " 231 "See https://pigweed.dev/pw_unit_test." 232 ) 233 else() # pw_unit_test_AUTOMATIC_RUNNER is provided. 234 # Define a target for running the test. The target creates a stamp file to 235 # indicate successful test completion. This allows running tests in parallel 236 # with Ninja's full dependency resolution. 237 if(NOT "${pw_unit_test_AUTOMATIC_RUNNER_TIMEOUT_SECONDS}" STREQUAL "") 238 set(optional_timeout_arg 239 "--timeout" "${pw_unit_test_AUTOMATIC_RUNNER_TIMEOUT_SECONDS}") 240 endif() 241 if(NOT "${pw_unit_test_AUTOMATIC_RUNNER_ARGS}" STREQUAL "") 242 set(optional_runner_args "--" "${pw_unit_test_AUTOMATIC_RUNNER_ARGS}") 243 endif() 244 add_custom_command( 245 COMMAND 246 python3 -m pw_unit_test.test_runner 247 --runner "${pw_unit_test_AUTOMATIC_RUNNER}" 248 --test "$<TARGET_FILE:${NAME}.bin>" 249 ${optional_timeout_arg} 250 ${optional_runner_args} 251 COMMAND 252 "${CMAKE_COMMAND}" -E touch "${NAME}.stamp" 253 DEPENDS 254 "${NAME}.bin" 255 OUTPUT 256 "${NAME}.stamp" 257 ) 258 add_custom_target("${NAME}.run" DEPENDS "${NAME}.stamp") 259 add_dependencies("${NAME}" "${NAME}.run") 260 endif() 261 262 if(arg_GROUPS) 263 pw_add_test_to_groups("${NAME}" ${arg_GROUPS}) 264 endif() 265endfunction(pw_add_test_generic) 266 267# pw_add_test_group: Defines a collection of tests or other test groups. 268# 269# Creates the following targets: 270# 271# {NAME} depends on ${NAME}.run if pw_unit_test_AUTOMATIC_RUNNER is set, else 272# it depends on ${NAME}.bin 273# {NAME}.bundle depends on ${NAME}.bundle.run if pw_unit_test_AUTOMATIC_RUNNER 274# is set, else it depends on ${NAME}.bundle.bin 275# {NAME}.lib depends on ${NAME}.bundle.lib. 276# {NAME}.bin depends on the provided TESTS's <test_dep>.bin targets. 277# {NAME}.run depends on the provided TESTS's <test_dep>.run targets if 278# pw_unit_test_AUTOMATIC_RUNNER is set, else it fails to build. 279# {NAME}.bundle.lib contains the provided tests bundled as a library target, 280# which can then be linked into a test executable. 281# {NAME}.bundle.bin standalone executable which contains the bundled tests. 282# {NAME}.bundle.run runs the {NAME}.bundle.bin test bundle executable after 283# building it if pw_unit_test_AUTOMATIC_RUNNER is set, else 284# it fails to build. 285# 286# Required Arguments: 287# 288# NAME - The name of the executable target to be created. 289# TESTS - pw_add_test targets and pw_add_test_group bundles to be included in 290# this test bundle 291# 292function(pw_add_test_group NAME) 293 pw_parse_arguments( 294 NUM_POSITIONAL_ARGMENTS 295 1 296 MULTI_VALUE_ARGS 297 TESTS 298 REQUIRED_ARGS 299 TESTS 300 ) 301 302 set(test_lib_targets "") 303 set(test_bin_targets "") 304 set(test_run_targets "") 305 foreach(test IN LISTS arg_TESTS) 306 list(APPEND test_lib_targets "${test}.lib") 307 list(APPEND test_bin_targets "${test}.bin") 308 list(APPEND test_run_targets "${test}.run") 309 endforeach() 310 311 # This produces ${NAME}.bundle, ${NAME}.bundle.lib, ${NAME}.bundle.bin, and 312 # ${NAME}.bundle.run. 313 pw_add_test("${NAME}.bundle" 314 PRIVATE_DEPS 315 ${test_lib_targets} 316 ) 317 318 # Produce ${NAME}.lib. 319 pw_add_library_generic("${NAME}.lib" INTERFACE 320 PUBLIC_DEPS 321 ${NAME}.bundle.lib 322 ) 323 324 # Produce ${NAME}.bin. 325 add_custom_target("${NAME}.bin") 326 add_dependencies("${NAME}.bin" ${test_bin_targets}) 327 328 # Produce ${NAME} and ${NAME}.run. 329 add_custom_target("${NAME}") 330 if("${pw_unit_test_AUTOMATIC_RUNNER}" STREQUAL "") 331 # Test runner is not provided, only build the executable. 332 add_dependencies("${NAME}" "${NAME}.bin") 333 334 pw_add_error_target("${NAME}.run" 335 MESSAGE 336 "Attempted to build ${NAME}.run which is not available because " 337 "pw_unit_test_AUTOMATIC_RUNNER has not been configured. " 338 "See https://pigweed.dev/pw_unit_test." 339 ) 340 else() # pw_unit_test_AUTOMATIC_RUNNER is provided, build and run the test. 341 add_custom_target("${NAME}.run") 342 add_dependencies("${NAME}.run" ${test_run_targets}) 343 344 add_dependencies("${NAME}" "${NAME}.run") 345 endif() 346endfunction(pw_add_test_group) 347 348# Adds a test target to the specified test groups. Test groups can be built with 349# the pw_tests_GROUP_NAME target or executed with the pw_run_tests_GROUP_NAME 350# target. 351function(pw_add_test_to_groups TEST_NAME) 352 foreach(group IN LISTS ARGN) 353 if(NOT TARGET "pw_tests.${group}") 354 add_custom_target("pw_tests.${group}") 355 add_custom_target("pw_run_tests.${group}") 356 endif() 357 358 add_dependencies("pw_tests.${group}" "${TEST_NAME}.bin") 359 add_dependencies("pw_run_tests.${group}" "${TEST_NAME}.run") 360 endforeach() 361endfunction(pw_add_test_to_groups) 362