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